정확한 세그먼트 이름을 알 수 없는 경우, 동적 데이터를 기반으로 경로를 생성하여 요청 시 동적으로 채우거나 빌드 시 미리 렌더링할 수 있다.
동적 세그먼트는 폴더 이름을 대괄호([ ])로 묶어 생성할 수 있다. [id] 또는 [slug]와 같은 방식이다. 이렇게 생성된 동적 세그먼트는 params 속성으로 layout, page, route, 그리고 generateMetadata 함수에 전달된다.
예를 들어, 블로그에는 app/blog/[slug]/page.js 경로가 포함될 수 있다. 여기서 [slug]는 블로그 게시물에 대한 동적 세그먼트이다.
export default function Page({ params }: { params: { slug: string } }) {
return <div>My Post: {params.slug}</div>
}
| Route |
Example |
params |
| 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' } |
generateStaticParams
generateStaticParams는 동적 경로를 빌드 시점에 미리 생성해서 성능을 최적화한다. 사용자가 페이지를 요청할 때, 서버가 실시간으로 데이터를 처리하지 않고 미리 만들어둔 페이지를 제공한다.
이 함수의 주요 이점은 요청을 자동으로 캐싱해서 동일한 요청을 한 번만 처리한다는 점이다. 이를 통해 빌드 시간도 줄어들고 성능이 향상된다.
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json());
return posts.map((post) => ({
slug: post.slug, // [slug]에 해당하는 값
}));
}
export default function BlogPostPage({ params }: { params: { slug: string } }) {
return <div>Blog Post: {params.slug}</div>;
}
Catch-all Segments
동적 세그먼트는 대괄호 안에 줄임표(...)를 추가하여 이후의 모든 세그먼트를 포함하는 방식으로 확장할 수 있다. 이를 Catch-all Segments라고 한다.
| Route |
Example |
params |
| app/shop/[...slug]/page.js |
/blog/a |
{ slug: ['a'] } |
| app/shop/[...slug]/page.js |
/blog/a/b |
{ slug: ['a', 'b'] } |
| app/shop/[...slug]/page.js |
/blog/a/b/c |
{ slug: ['a', 'b', 'c'] } |
Optional Catch-all Segments
Catch-all Segments는 매개변수를 이중 대괄호([[...]])로 묶어서 선택적으로 만들 수 있다. 이 방식은 Optional Catch-all Segments라고 하며, 매개변수가 없는 경로도 포함해서 처리할 수 있다.
| Route |
Example |
params |
| 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'] } |