<aside> <img src="/icons/search_gray.svg" alt="/icons/search_gray.svg" width="40px" />
TL;DR
[slug]
: 단일 경로 세그먼트 포착[...slug]
: 다중 경로 세그먼트 포착(Catch-All)[[...slug]]
: 루트 경로를 포함한 모든 경로를 선택적으로 포착(Optional Catch-All)
</aside>정확한 세그먼트 이름을 미리 알 수 없을 때 폴더 이름을 대괄호로 감싸면 다이나믹 세그먼트로 작동. 세그먼트 이름은 layout, page 또는 route 파일에서 params
프롭으로 값 조회 가능.
Route | URL Example | params |
---|---|---|
app/blog/[slug]/page.js |
/blog/a |
{ slug: 'a' } |
app/blog/[slug]/page.js |
/blog/b |
{ slug: 'b' } |
🔍 루트 경로(blog/)에 페이지 없으면 접근 불가.
다이나믹 세그먼트를 확장하여 경로의 여러 세그먼트를 포착하려면 폴더 이름 앞에 점 3개(…)를 추가하여 Catch-All 세그먼트로 설정 가능. 이를 통해 여러 경로를 하나의 세그먼트로 처리할 수 있으며 params
는 배열 형태로 전달됨.
Route | URL Example | params |
---|---|---|
app/blog/[...slug]/page.js |
/blog/a |
{ slug: ['a'] } |
app/blog/[...slug]/page.js |
/blog/a/b |
{ slug: ['a', 'b'] } |
🔍 루트 경로(blog/)에 페이지 없으면 접근 불가.
Catch-All 세그먼트를 선택적으로 적용할 땐 대괄호 2개를 사용하여 Optional Catch-All 세그먼트로 설정 가능. 루트 경로와 하위 경로를 모두 처리할 수 있으며, 루트 경로에선 params.slug
값이 undefined
로 전달됨.
Route | URL Example | params |
---|---|---|
app/blog/[[...slug]]/page.js |
/blog |
{ slug: undefined } |
app/blog/[[...slug]]/page.js |
/blog/a |
{ slug: ['a'] } |
app/blog/[[...slug]]/page.js |
/blog/a/b |
{ slug: ['a', 'b'] } |
🔍 루트 경로(blog/)로 접근하면 app/blog/[[...slug]]/page.js
로 매핑됨.
**⚠️ Optional Catch-All 세그먼트 사용시 동일 루트 폴더에 별도의 페이지 파일은 추가 못함**
📦 app/
└─ blug/
├─ [[...slug]]/
│ └─ page.tsx
└─ page.tsx (❌ 불가)
generateStaticParams
를 사용하면 동적 경로 기반의 정적 페이지를 빌드 타임에 미리 생성할 수 있으며, 이 함수 안에서 fetch
로 가져온 데이터는 자동으로 메모이징된다. 이후 다른 페이지나 레이아웃 등에서 동일한 arguments를 사용하여 보낸 요청은 추가적인 네트워크 요청 없이 값을 재사용한다. 이를 통해 빌드 시간을 효율적으로 단축할 수 있다.
export async function generateStaticParams() {
// API 호출 또는 데이터베이스 쿼리 등으로 동적 경로 목록을 가져옴
const posts = await fetch('<https://api.example.com/posts>').then((res) => res.json());
return posts.map((post) => ({
slug: post.slug, // slug는 동적 경로 파라미터에 해당
}));
}
export default function Page({ params }) {
return <div>Post: {params.slug}</div>;
}