📝Next.js란
Next.js는 React 기반의 웹 애플리케이션을 쉽게 구축할 수 있게 해주는 JavaScript 프레임워크입니다
웹 개발자들이 React를 사용하여 서버 사이드 렌더링(SSR) 및 정적 생성(Static Site Generation, SSG)과 같은 기능을 구현할 수 있도록 도와줍니다
React의 경우 Client Side Rendering으로 인해 봇이 사이트에 접속해서 바로 수집할 경우 아무것도 없기 때문에 검색엔진 최적화에 문제가 있었다 Next.js는 이부분을 해결해줍니다 → SEO 최적화
📝용어
세그먼트(Segment) → 세그먼트는 URL에서 '/'을 기준으로 나누어지는 URL 조각
📝최상위 폴더
app | 앱 라우터 → 14버전 권장 |
pages | 페이지 라우터 |
public | 정적 데이터 |
📝최상위 파일
next.config.js | Next.js 구성 파일 |
package.json | 프로젝트 종속성 관리 및 스크립트 |
middleware.ts | Next.js 요청 미들웨어 (Request 조작 후 원하는 Response 제공) → redirect, rewrite 등… |
.env | 환경 변수 |
.env local | 로컬 환경 변수 |
.env.production | 프로덕션 환경 변수 |
.eslintrc.json | ESLint용 구성 파일 |
tsconfig.json | TypeScript용 구성 파일 (설정) |
📝Route 파일 규칙
layout | 최상위 호출 파일 |
page | 페이지 |
loading | UI로딩 |
not-found | not found 페이지 |
error | Error 페이지 |
global-error | 전역 Error 페이지 |
route | API 엔드포인트 (서버 API 역할) |
template | layout하고 무슨 차인지 잘 모르겠음 |
📝Route 방법 (폴더)
folder | 라우팅 세그먼트 |
folder/folder | 하위 라우팅 세그먼트 |
📝동적 Route
[folder] | 동적 라우팅 세그먼트 [아래에서 상세 설명] |
[…folder] | 동적 포괄 세그먼트 [아래에서 상세 설명] app/shop/[...slug]/page.js → /shop/a, shop/a/b, shop/a/b/c [해당 URL 요청도 감지] |
[[…folder]] | 선택적 포괄 세그먼트 [아래에서 상세 설명] app/shop/[[...slug]]/page.js → /shop, /shop/a, shop/a/b, shop/a/b/c [해당 URL 요청도 감지] |
📝라우팅 그룹 및 개인 폴더
(folder) | 라우팅에 영향 주지않는 그룹 경로 → 라우팅 경로는 주고 싶지 않지만 폴더 구분을 하고 싶은 경우 (같은 라우팅 경로이지만 서로 다른 레이아웃을 줄 수 있다) [아래에서 상세 설명] |
_folder | 개인 폴더로 모든 라우팅에서 제외되는 폴더 |
📝병렬 및 경로 가로채기
@folder | 페이지를 Include하는 것처럼 각 병렬 폴더에 있는 page.tsx를 layout에서 import 가능 [다음장에서 상세 설명] |
(.)folder | 동일한 수준의 세그먼트 일치 [다음장에서 상세 설명] |
(..)folder | 한 수준 위의 세그먼트 일치 |
(..)(..)folder | 두 수준 위의 세그먼트 일치 |
(…)folder | 루트 app 디렉토리의 세그먼트 일치 |
📝아이콘
favicon | 웹 페이지 탭에 표시 시킬 아이콘 (/app route segment에 존재) [default] |
icon | 웹 페이지 탭에 표시 시킬 아이콘 (코드로 생성 및 세밀한 조정 가능) |
apple-icon | apple기기에서 보여질 아이콘 (코드로 생성도 가능) |
opengraph-image | 링크 공유하기 할 때 어떤형식으로 보여줄지에 대한 프로토콜로 (아이콘 등에 대한 설정 가능) → 카카오톡은 이런식으로 등… [코드로 생성 가능] |
twitter-image | 트위터에서 링크 공유할 때 보여줄 형식 (코드로 생성 가능) |
📝SEO
sitemap | 크롤링 봇이 어떤 경로로 수집할 수 있게 알려주는 가이드라인 (코드로도 생성 가능) |
robots | 크롤링 봇에 대한 여부 및 다양한 설정 |
📝라우팅
경로를 찾으려면 해당 폴더에 page.js가 존재해야합니다 (/dashboard/settings로 접근시 page.tsx가 열린다)
📝루트 레이아웃 (app/layout.tsx)
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
- 반드시 필요한 레이아웃으로 가장 처음에 실행된다
- html과 body에 대한 정의가 꼭 들어가야한다
📝중첩 레이아웃
레이아웃은 중첩해서 사용이 가능하다
위에 예제를 보면 /dashbaord로 URL 접근시 app/layout.tsx의 최상단 레이아웃 안에 children에 dashboard/layout.tsx가 그려지게 되고 page.tsx가 존재할 시 dashboard/layout.tsx의 children에 dashboard/page.tsx가 그려지게 된다
📝Link (페이지 연결)
import Link from 'next/link'
export default function PostList({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
)
}
<a> 제공하는 내장구성요소로 Next.js에서 경로 간을 탐색하는 기본 방법이다
'use client'
import { usePathname } from 'next/navigation'
import Link from 'next/link'
export function Links() {
const pathname = usePathname()
return (
<nav>
<ul>
<li>
<Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
Home
</Link>
</li>
<li>
<Link
className={`link ${pathname === '/about' ? 'active' : ''}`}
href="/about"
>
About
</Link>
</li>
</ul>
</nav>
)
}
usePathname을 통해 현재 경로 URL을 가져와서 동적 처리가 가능
<Link href="/dashboard#settings">Settings</Link>
// Output
<a href="/dashboard#settings">Settings</a>
페이지 이동시 스크롤 위치를 #을 통해 지정할 수 있다
// next/link
<Link href="/dashboard" scroll={false}>
Dashboard
</Link>
페이지 이동후 스크롤 비활성화 가능
📝useRouter (페이지 연결)
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/dashboard')}>
Dashboard
</button>
)
클라이언트 구성 요소 내에서만 사용할 수 있다 → use client 선언 필요
📝페이지 연결 작동 방식
서버에서 애플리케이션 코드는 경로 세그먼트 별로 자동으로 코드 분할되고 해당 경로를 미리 가져오고 캐시를 한다 즉, 페이지를 다시 로드하지 않고 변경된 세그먼트만 다시 렌더링하여 성능을 향상시킨다
사용자는 경로 방문 전에 백그라운드에서 경로를 미리 로드한다 → 예를 들면 네이버 메인페이지에 다양한 링크들이 있다 메인페이지에 들어간 후 오프라인 설정한 다음 메인페이지에 있는 링크에 들어가면 로드가 된다 (전부가 로드 되는 건 아님 캐싱에 있는 애들이나 정적으로 만들어진 애들을 로드)
📝라우팅 및 탐색 작동 방식
서버에서 애플리케이션 코드는 경로 세그먼트 별로 자동으로 코드 분할되고 해당 경로를 미리 가져오고 캐시를 한다 즉, 페이지를 다시 로드하지 않고 변경된 세그먼트만 다시 렌더링하여 성능을 향상시킨다
프리패칭 (Prefetching)
사용자는 경로 방문 전에 백그라운드에서 경로를 미리 로드한다 → 예를 들면 네이버 메인페이지에 다양한 링크들이 있다 메인페이지에 들어간 후 오프라인 설정한 다음 메인페이지에 있는 링크에 들어가면 로드가 된다 (전부가 로드 되는 건 아님 캐싱에 있는 애들이나 정적으로 만들어진 애들을 로드)
Next에서 경로를 미리 가져오는 방법은 두가지가 있다
- <Link> 컴포넌트 → 뷰포트에 표시되면 자동으로 미리 가져온다
- 정적 경로일 경우
- prefetch의 true가 기본값이고 전체 경로가 프리패치가 되며 캐시도 된다
- 동적 경로일 경우
- prefetch의 true가 기본값이고 loading.tsx가 먼저 프리패치가 된다 → 그 이후에 패치작업을 미리하거나 하는 건 아닌 거 같음
- 정적 경로일 경우
- router.prefetch → useRouter 기능을 이용한 프리패치 방식
- <button onClick={()=>{ router.prefetch('/어쩌구') }}>버튼</button> router.prefetch('/')
무작정 사용하는 건 위험할 수도 있다 → 현재 페이지와 관련이 없는 링크를 포함하는 경우, 불필요한 리소스를 사전 로드하지 않아 초기 페이지 로딩 속도를 개선시키는 방법도 존재
캐싱
Next.js에는 라우터 캐시(Router Cache) 라는 메모리 내 클라이언트 측 캐시가 있습니다
사용자가 앱을 탐색할 때 미리 가져온 경로 세그먼트와 방문한 경로의 React Server 구성 요소 페이로드가 캐시에 저장
부분 렌더링
변경되는 경로의 세그먼트만 변경되며 나머지 세그먼트는 그대로 보존된다
스크롤 유지
이동한 뒤에 뒤로가기로 이동해도 이동하기 전의 스크롤을 유지시킨다
📝Route Group (라우트 그룹)
URL 경로에 매핑되지 않게 경로 그룹을 표시할 수 있다 (folderName)으로 작성
공통된 레이아웃을 가지지만 URL 경로를 추가하고 싶지 않은 것들 끼리 같은 그룹으로 묶어서 layout.tsx의 중복을 막을 수도 있다 → shop영역으로 묶은 account와 cart는 layout.tsx를 공통으로 가질 수 있다
📝여러 루트 레이아웃 생성
생성하려면 최상위 layout.tsx 파일을 제거하고 각 경로 그룹내에 파일을 추가 물론 각 레이아웃 파일은 <html> <body>가 존재해야한다
📝동적경로 (Dynamic Route)
export default function Page({ params }: { params: { slug: string } }) {
return <div>My Post: {params.slug}</div>
}
대괄호를 묶어서 생성한다 [foldername] → 예) [id]
예제
app/blog/[slug]/page.js | /blog/a | { slug: 'a' } |
app/blog/[slug]/page.js | /blog/b | { slug: 'b' } |
app/blog/[slug]/page.js | /blog/c | { slug: 'c' } |
📝포괄 세그먼트 (Catch-all Segments)
예제
app/shop/[...slug]/page.js | /shop/a | { slug: ['a'] } |
app/shop/[...slug]/page.js | /shop/a/b | { slug: ['a', 'b'] } |
app/shop/[...slug]/page.js | /shop/a/b/c | { slug: ['a', 'b', 'c'] } |
대괄호 안에 줄임표를 이용해 모든 후속 세그먼트로 확장 가능
예제
app/shop/[[...slug]]/page.js | /shop | {} |
app/shop/[[...slug]]/page.js | /shop/a | { slug: ['a'] } |
app/shop/[[...slug]]/page.js | /shop/a/b | { slug: ['a', 'b'] } |
app/shop/[[...slug]]/page.js | /shop/a/b/c | { slug: ['a', 'b', 'c'] } |
이중 대괄호를 이용해 기본 URL도 동적 생성이 가능하다
📝정적 매개변수 생성 (SSG)
export function generateStaticParams() {
return [{ slug: '1' }, { slug: '2' }, { slug: '3' }]
}
export default function Page({ params }: { params: { slug: string } }) {
const { slug } = params
console.log(params);
return <div>
{slug}
</div>
}
// ----- 또 다른 예제 방식 ----- //
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
return posts.map((post) => ({
slug: post.slug,
}))
}
1.html, 2.html, 3.html이 생성이 된다 → 이미 만들어져 있기 때문에 속도가 빠르다
📝에러 핸들링 (Error Handling)
라우팅할 폴더안에 error.tsx를 만들면 해당 라우팅에서 에러가 날때 error.tsx를 보여준다 (ErrorBoundray라는 컴포넌트가 자동적으로 생성됨)
error.tsx
'use client'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
)
}
Error 컴포넌트는 이렇게 만들며 reset이라는 기능도 지원하는데 에러가 발생했을 때 어떤 핸들링을 할지에 대한 것이다 (참고로 템플릿 및 레이아웃의 오류는 포착하지 않습니다)
루트 레이아웃 오류 처리의 경우는 /app/global-error.tsx를 이용해 처리가 가능합니다 (전체적인 Error에 대한 핸들링)
📝병렬 경로 (Parallel Routes)
병렬 라우팅을 사용하면 각각의 컴포넌트들이 독립적으로 렌더링이 됩니다 폴더에 @을 붙여서 병렬 처리 가능한 상태로 변경합니다 [서스펜스 로딩과 비슷한 느낌이지만 컴포넌트가 아닌 페이지 단위인 것 같다] → 병렬처리가 되는지 실제로 테스트가 필요하긴 함
export default function Layout(props: {
children: React.ReactNode
analytics: React.ReactNode
team: React.ReactNode
}) {
return (
<>
{props.children}
{props.team}
{props.analytics}
</>
)
}
@team/members → /members로 접근 가능하고 props에 여러개 적는 이름은 @에 적은 이름과 동일해야한다
default.js
├── @analytics
│ └── page.tsx
├── @team
│ └── settings
│ └── page.tsx
├── layout.tsx
└── page.tsx
위와 같은 트리 구조에서 “/”를 호출하면 404가 렌더링이 되는데 settings폴더 안에 page.tsx가 있기 때문이다 [@team 폴더 안에 page.tsx가 있어야 한다] 이럴 경우 @team안에 default.tsx를 만들어주면된다 (@을 슬롯이라고 부른다)
├── @analytics
│ └── page.tsx
├── @team
│ ├── default.tsx ✅ 추가
│ └── settings
│ └── page.tsx
├── layout.tsx
└── page.tsx
/**------------------------------------------**/
export default function Default() {
return null;
}
이렇게 할 경우 @analytics에 해당하는 page 부분만 보이고 일치하지 않는 부분은 404를 렌더링 하지 않도록 한다
만약 “/settings”를 호출하면 404가 렌더링 되는데 settings와 같은 수준의 page.tsx가 존재하지 않기 때문입니다
(@analytics와 layout.tsx에서 정의한 children도 존재하지 않음)
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default async function Layout(props: {
//...
auth: React.ReactNode
}) {
const loginSegments = useSelectedLayoutSegment('auth')
// ...
}
useSelectedLayoutSegment(s) → layout의 URL 경로를 가져온다 (layout.tsx에서만 사용 가능하며 use clinet 사용 필수)
📝경로 가로채기 (Intercepting Routes)
경로를 가로채면 현재 레이아웃 내 애플리케이션의 다른 부분에서 경로를 로드할 수 있습니다
예를 들면 피드에서 사진을 클릭하면 피드에 오버레이되어 사진을 모달로 표시할 수 있습니다. 이 경우 Next.js는 /photo/123경로를 가로채서 URL을 마스크하고 이를 오버레이합니다
- (.)
- 동일한 수준 의 세그먼트를 일치시킬 시
- (..)
- 한 수준 위의 세그먼트와 일치시킬 시
- (..)(..)
- 두 수준 위의 세그먼트와 일치시킬 시
- (...)루트 app
- 디렉터리 의 세그먼트를 일치시킬 시
layout에서 /feed에서 Link로 photo/2로 클릭시 접근할 때 (..)photo/[id]/page로 연결 시킨다
(..)인 이유는 feed위로 올라가야 photo랑 동등한 수준이 되기 때문에 그렇다
예제 홈페이지 → https://nextgram.vercel.app/
예제 소스 → https://github.com/vercel/nextgram.git
병렬 경로와 경로 가로채기를 이용해 위의 예제 소스를 통해 쉽게 모달을 구현할 수 있다고 한다
🔗 참고 및 출처
https://velog.io/@ckstn0777/Next.js-13-Parallel-Routes-Intercepting-Routes
https://velog.io/@jay/Parallel-Routes#layoutjs-에서-parallel-route를-사용해-modal-띄우기
'[Next.js] > [Next 14]' 카테고리의 다른 글
[Next 14] 정적 데이터, 이미지, 폰트, 스크립트, 정적 메타데이터, Opengraph, 동적 메타데이터 (1) | 2024.01.02 |
---|---|
[Next 14] 외부 라이브러리, 런타임(Runtime), CSS Style (0) | 2024.01.02 |
[Next 14] Server Component (SSR), Client Component (CSR) (1) | 2024.01.02 |
[Next 14] 미들웨어 (Middleware) (0) | 2024.01.02 |
[Next 14] 로딩, 서스펜스, 스트리밍 (0) | 2024.01.01 |