error.js
는 경로 세그먼트에 대한 오류 UI 경계를 정의한다.
// app/dashboard/error.tsx
'use client' // Error components must be Client Components
import { useEffect } from 'react'
export default function Error({
error,
reset,
}: {
error: Error
reset: () => void
}) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error)
}, [error])
return (
<div>
<h2>Something went wrong!</h2>
<button
onClick={
// Attempt to recover by trying to re-render the segment
() => reset()
}
>
Try again
</button>
</div>
)
}
error
error
객체의 인스턴스다. 이 오류는 서버나 클라이언트에서 발생할 수 있다.
reset
응답을 반환하지 않는 error boundary를 재설정하는 함수다.
error.js
boundary는 클라이언트 컴포넌트여야 한다.
error.js
boundary는 같은 세그먼트의 layout.js
컴포넌트에서 발생한 오류를 처리하지 않는다. 이는 error boundary가 해당 레이아웃 컴포넌트 내에 중첩되어 있기 때문이다.
특정 레이아웃의 오류를 처리하려면 error.js
파일을 레이아웃의 상위 세그먼트에 배치한다.
루트 레이아웃이나 템플릿 내에서 오류를 처리하려면 error.js
의 변형인 app/global-error.js
를 사용한다.
root layout.js
에서 발생하는 오류를 특별히 처리하려면, 루트 app
디렉토리에 위치한 error.js
의 변형인 app/global-error.js
를 사용한다.
// app/global-error.js
'use client'
export default function GlobalError({
error,
reset,
}: {
error: Error
reset: () => void
}) {
return (
<html>
<body>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</body>
</html>
)
}
global-error.js
는 활성화되었을 때 루트 layout.js
를 대체하므로 자체 <html>
및 <body>
태그를 정의해야 한다.레이아웃은 라우트 간에 공유되는 UI다.
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>{children}</section>
}
루트 레이아웃은 루트 앱 디렉토리에서 가장 상위에 위치한 레이아웃이다. <html>
및 <body>
태그 및 다른 전역적으로 공유되는 UI를 정의하는 데 사용된다.
// app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
레이아웃 컴포넌트는 children
prop을 받아서 사용해야 한다. 렌더링 중에 children
은 레이아웃이 감싸고 있는 라우트 세그먼트로 채워진다. 주로 자식 레이아웃 (있는 경우) 또는 페이지의 컴포넌트일 것이다. 그러나 해당되는 경우 Loading
이나 Error
와 같은 다른 특수 파일일 수도 있다.
루트 세그먼트에서 해당 레이아웃까지의 동적 라우트 매개변수 객체다.
Example | URL | params |
---|---|---|
app/dashboard/[team]/layout.js | /dashboard/1 | { team: '1' } |
app/shop/[tag]/[item]/layout.js | /shop/1/2 | { tag: '1', item: '2' } |
app/blog/[...slug]/layout.js | /blog/1/2 | { slug: ['1', '2'] } |
// app/shop/[tag]/[item]/layout.tsx
export default function ShopLayout({
children,
params,
}: {
children: React.ReactNode
params: {
tag: string
item: string
}
}) {
// URL -> /shop/shoes/nike-air-max-97
// `params` -> { tag: 'shoes', item: 'nike-air-max-97' }
return <section>{children}</section>
}
레이아웃은 searchParams
prop을 받지 않는다.
page와 달리 layout 컴포넌트는 searchParams
prop을 받지 않는다. 이는 공유된 레이아웃이 탐색 중에 다시 렌더링되지 않기 때문에 navigation 사이에 오래된 searchParams
가 발생할 수 있기 때문이다.
클라이언트 측 navigation을 사용할 때, Next.js는 두 라우트 사이에서 공통 레이아웃 아래에 있는 페이지의 일부만 자동으로 렌더링한다.
다음 디렉토리 구조의 예를 들어보면, dashboard/layout.tsx
는 /dashboard/settings
및 /dashboard/analytics
의 공통 레이아웃이다.
app
└── dashboard
├── layout.tsx
├── settings
│ └── page.tsx
└── analytics
└── page.js
/dashboard/settings
에서 /dashboard/analytics
로 이동할 때, /dashboard/analytics
의 page.tsx
가 서버에서 렌더링된다. 왜냐하면 이것은 변경된 UI이기 때문이다.
반면, dashboard/layout.tsx
는 두 라우트 사이에서 공통으로 사용되기 때문에 다시 렌더링되지 않는다.
이 성능 최적화는 레이아웃을 공유하는 페이지 간의 navigation을 빠르게 만들어준다. 왜냐하면 해당 페이지의 데이터 가져오기와 렌더링만 실행하면 되기 때문에 공유 레이아웃이 자체 데이터를 가져오는 라우트 전체를 실행할 필요가 없기 때문이다.
dashboard/layout.tsx
가 다시 렌더링되지 않기 때문에, 레이아웃 서버 컴포넌트의 searchParams
prop은 navigation 후에 오래된(stale) 상태가 될 수 있다.
대신에, Page의 searchParams
prop이나 Client 컴포넌트에서 useSearchParams
훅을 사용하여 클라이언트에서 최신 searchParams
와 함께 다시 렌더링되도록 한다.
app
디렉토리에는 루트 app/layout.js
가 포함되어야 한다.
루트 레이아웃은 <html>
및 <body>
태그를 정의해야 한다.
루트 레이아웃에는 수동으로 <title>
및 <meta>
와 같은 <head>
태그를 추가해서는 안된다. 대신에 <head>
요소의 스트리밍과 중복 제거와 같은 고급 요구 사항을 자동으로 처리하는 Metadata API
를 사용해야 한다.
route groups를 사용하여 여러 개의 루트 레이아웃을 생성할 수 있다.
여러 개의 루트 레이아웃을 통해 navigation할 경우 전체 페이지 로드가 발생합니다(클라이언트 측 navigation이 아닌 경우). 예를 들어, app/(shop)/layout.js
를 사용하는 /cart
에서 app/(marketing)/layout.js
를 사용하는 /blog
로 이동할 경우 전체 페이지 로드가 발생합니다. 이는 여러 개의 루트 레이아웃에만 해당됩니다.
loading.js
는 Suspense를 기반으로 한 즉시 로딩 상태를 생성할 수 있다.
기본적으로 이 파일은 서버 컴포넌트다. 그러나 "use client"
지시문을 통해 클라이언트 컴포넌트로 사용할 수도 있다.
// app/feed/loading.js
export default function Loading() {
// Or a custom loading skeleton component
return <p>'Loading...'</p>
}
not-found.js
은 라우트 세그먼트 내에서 notFound
함수가 throw될 때 UI를 렌더링하는 데 사용된다. 커스텀 UI를 제공하는 것 외에도, Next.js는 404 HTTP
상태 코드를 반환한다.
// app/blog/not-found.js
import Link from 'next/link'
export default function NotFound() {
return (
<div>
<h2>Not Found</h2>
<p>Could not find requested resource</p>
<p>
View <Link href="/blog">all posts</Link>
</p>
</div>
)
}
notFound()
오류를 예상하여 catch하는 것 외에도, 루트 app/not-found.js
파일은 전체 애플리케이션에서 일치하지 않는 URL을 처리한다. 이는 사용자가 앱에서 처리되지 않은 URL을 방문할 경우 app/not-found.js
파일에서 내보낸 UI가 표시됨을 의미한다.
page
는 라우트에 고유한 UI다.
params(선택적)
루트 세그먼트부터 해당 페이지까지의 동적 라우트 매개변수를 담고 있는 객체다. 예를 들면 다음과 같다.
Example | URL | params |
---|---|---|
app/shop/[slug]/page.js | /shop/1 | { slug: '1' } |
app/shop/[category]/[item]/page.js | /shop/1/2 | { category: '1', item: '2' } |
app/shop/[...slug]/page.js | /shop/1/2 | { slug: ['1', '2'] } |
searchParams(선택적)
현재 URL의 검색 매개변수를 포함하는 객체다. 예를 들면 다음과 같다.
URL | searchParams |
---|---|
/shop?a=1 | { a: '1' } |
/shop?a=1&b=2 | { a: '1', b: '2' } |
/shop?a=1&a=2 | { a: ['1', '2'] } |
searchParams
는 값을 미리 알 수 없는 동적 API다. 이를 사용하면 페이지가 요청 시 동적 렌더링으로 선택된다.
searchParams
는 URLSearchParams
인스턴스가 아닌 일반 JavaScript 객체를 반환한다.
Route Handlers를 사용하면 Web 요청 및 응답 API를 사용하여 특정 라우트에 대한 사용자 정의 요청 핸들러를 만들 수 있다.
route.js
을 사용하면 특정 라우트에 대한 사용자 정의 요청 핸들러를 생성할 수 있다. 다음 HTTP 메서드가 지원된다. GET
, POST
, PUT
, PATCH
, DELETE
, HEAD
및 OPTIONS
.
// route.js
export async function GET(request: Request) {}
export async function HEAD(request: Request) {}
export async function POST(request: Request) {}
export async function PUT(request: Request) {}
export async function DELETE(request: Request) {}
export async function PATCH(request: Request) {}
// If `OPTIONS` is not defined, Next.js will automatically implement `OPTIONS` and set the appropriate Response `Allow` header depending on the other methods defined in the route handler.
export async function OPTIONS(request: Request) {}
Route Handlers는 app
디렉토리 내에서만 사용할 수 있다. API Routes (pages
)와 Route Handlers (app
)를 함께 사용할 필요는 없으며, Route Handlers는 모든 사용 사례를 처리할 수 있어야 한다.
request
객체는 NextRequest 객체로, Web 요청 API의 확장이다. NextRequest
를 사용하면 수신된 요청에 대한 추가적인 제어를 할 수 있다. 이를 통해 cookies
에 쉽게 액세스하고 확장된 파싱된 URL 객체인 nextUrl
에 쉽게 접근할 수 있다.
// app/dashboard/[team]/route.js
export async function GET(request, context: { params }) {
const team = params.team // '1'
}
현재 context
의 유일한 값은 params
이며, 이는 현재 라우트의 동적 라우트 매개변수를 포함하는 객체다.
Route Handlers는 NextResponse
객체를 반환함으로써 Web 응답 API를 확장할 수 있다. 이를 통해 쿠키 설정, 헤더 설정, 리다이렉트 및 리라이트를 쉽게 처리할 수 있다.
Route Segment 옵션은 다음 변수들을 직접 내보내는 방식으로 Page, Layout 또는 Route Handler의 동작을 구성할 수 있도록 한다.
// layout.tsx / page.tsx / route.ts
export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'
export default function MyComponent() {}
현재 구성 옵션의 값은 정적으로 분석 가능해야 한다. 예를 들어, revalidate = 600
은 유효하지만 revalidate = 60 * 10
은 유효하지 않다.
dynamic
레이아웃이나 페이지의 동적 동작을 완전히 정적으로 또는 완전히 동적으로 변경한다.
// layout.tsx / page.tsx / route.ts
export const dynamic = 'auto'
// 'auto' | 'force-dynamic' | 'error' | 'force-static'
app
디렉토리의 새로운 모델은 page
디렉토리의 getServerSideProps
와 getStaticProps
의 바이너리 all-of-nothing 모델보다는 fetch
요청 수준의 세분화된 캐싱 제어를 선호한다. dynamic
옵션은 이전 모델로의 편리한 복귀 방법이며 간단한 마이그레이션 경로를 제공한다.
auto (기본값)
동적 동작으로 전환하지 않는 한 모든 컴포넌트가 동적 동작에 참여하지 않도록 최대한 캐싱한다.
force-dynamic
모든 fetch
요청의 캐싱을 비활성화하고 항상 재검증하여 레이아웃이나 페이지의 동적 렌더링과 동적 데이터 가져오기를 강제한다. 이 옵션은 다음과 동일하다.
- `pages` 디렉토리의 `getServerSideProps()`
- 레이아웃이나 페이지의 모든 `fetch()` 요청의 옵션을 `{ cache: 'no-store', next: { revalidate: 0 } }`로 설정하기.
- 세그먼트 구성을 `export const fetchCache = 'force-no-store'`로 설정하기.
error
동적 함수나 동적 fetch
사용 시 오류가 발생하여 레이아웃이나 페이지의 정적 렌더링과 정적 데이터 가져오기를 강제한다. 이 옵션은 다음과 동일하다.
- `pages` 디렉토리의 `getStaticProps()`
- 레이아웃이나 페이지의 모든 `fetch()` 요청의 옵션을 `{ cache: 'force-cache' }`로 설정하기.
- 세그먼트 구성을 `fetchCache = 'only-cache', dynamicParams = false`로 설정하기.
- `dynamic = 'error'`는 `dynamicParams`의 기본값을 `true`에서 `false`로 변경한다.
`generateStaticParams`에 의해 생성되지 않은 동적 매개변수를 가진 동적으로 렌더링되는 페이지로 다시 돌아가려면 `dynamicParams = true`를 수동으로 설정해야 한다.
force-static
cookies()
, headers()
, useSearchParams()
를 비워진 값으로 반환하여 레이아웃이나 페이지의 정적 렌더링과 정적 데이터 가져오기를 강제한다.
dynamicParams
generateStaticParams
로 생성되지 않은 동적 세그먼트가 방문될 때 어떤 동작이 수행되는지를 제어한다.
// layout.tsx / page.tsx
export const dynamicParams = true // true | false,
true (기본값)
generateStaticParams`에 포함되지 않은 동적 세그먼트는 필요할 때 생성된다.
false
generateStaticParams
에 포함되지 않은 동적 세그먼트는 404를 반환한다.
이 옵션은 페이지 디렉토리의 getStaticPaths
의 fallback: true | false | blocking
옵션을 대체한다.
dynamicParams = true
인 경우 세그먼트는 스트리밍 서버 렌더링을 사용한다.
dynamic = 'error'
와 dynamic = 'force-static'
을 사용하면 dynamicParams
의 기본값이 false
로 변경된다.
revalidate
레이아웃이나 페이지의 기본 재유효화 시간을 설정한다. 이 옵션은 개별 fetch
요청에서 설정한 revalidate
값에 대체되지 않는다.
// layout.tsx / page.tsx / route.ts
export const revalidate = false
// false | 'force-cache' | 0 | number
false (기본값)
'force-cache'
로 캐시 옵션을 설정하거나 동적 함수가 사용되기 전에 발견된 모든 fetch 요청을 캐시하는 기본 heuristic. 사실상 revalidate: Infinity
와 동일하며, 리소스를 무기한으로 캐시해야 함을 의미한다. 개별 fetch
요청에서 cache: 'no-store'
또는 revalidate: 0
을 사용하여 캐시를 피하고 라우트를 동적으로 렌더링하는 것도 가능하다. 또는 revalidate
을 라우트의 기본값보다 작은 양의 양수로 설정하여 라우트의 재유효화 빈도를 증가시킬 수 있다.
0
동적 함수나 동적 데이터 가져오기가 발견되지 않아도 레이아웃이나 페이지가 항상 동적으로 렌더링되도록 보장한다. 이 옵션은 캐시 옵션을 설정하지 않은 fetch
요청의 기본값을 'no-store'
로 변경하지만, 'force-cache'
로 옵트인하거나 양수 revalidate
를 사용하는 fetch
요청은 변경하지 않는다.
number (초 단위)
레이아웃이나 페이지의 기본 재유효화 빈도를 n
초로 설정한다.
revalidate 빈도
revalidate
값이 전체 라우트의 재유효화 빈도를 결정한다. 이를 통해 하위 페이지도 상위 레이아웃과 동일한 빈도로 재유효화된다.fetch
요청은 라우트의 기본 revalidate
보다 낮은 revalidate
를 설정하여 전체 라우트의 재유효화 빈도를 증가시킬 수 있다. 이를 통해 특정 기준에 따라 특정 라우트에 대해 동적으로 더 빈번한 재유효화를 선택적으로 사용할 수 있다.runtime
// layout.tsx / page.tsx / route.ts
export const runtime = 'nodejs'
// 'edge' | 'nodejs'
preferredRegion
// layout.tsx / page.tsx / route.ts
export const preferredRegion = 'auto'
// 'auto' | 'global' | 'home' | ['iad1', 'sfo1']
preferredRegion
및 지원되는 지역에 대한 지원은 배포 플랫폼에 따라 다르다.
preferredRegion
이 지정되지 않은 경우, 가장 가까운 상위 레이아웃의 옵션을 상속한다.
루트 레이아웃은 모든 지역을 기본값으로 갖는다.
generateStaticParams
generateStaticParams
함수는 동적 라우트 세그먼트와 함께 사용하여, 요청 시간이 아닌 빌드 시간에 정적으로 생성될 경로 세그먼트 매개변수 목록을 정의하는 데 사용될 수 있다.
파일 기반의 메타데이터는 라우트 세그먼트에 특수한 메타데이터 파일을 추가하여 정의할 수 있다.
각 파일 규칙은 정적 파일(예: opengraph-image.jpg
) 또는 코드를 사용하여 파일을 생성하는 동적 변형(예: opengraph-image.js
)을 사용하여 정의할 수 있다.
파일이 정의되면 Next.js는 자동으로 파일을 제공하며 (생산 환경에서는 캐싱을 위해 해시가 포함됨) 관련된 헤드 요소를 올바른 메타데이터로 업데이트한다. 이는 자산의 URL, 파일 유형 및 이미지 크기와 같은 정보를 포함한다.
File Conventions: Metadata Files
[출처]
https://nextjs.org/docs/app/api-reference/file-conventions
드디어 마지막 글~ 감사합니다!