<aside> <img src="/icons/search_gray.svg" alt="/icons/search_gray.svg" width="40px" />

TL;DR

Dynamic Segments

정확한 세그먼트 이름을 미리 알 수 없을 때 폴더 이름을 대괄호로 감싸면 다이나믹 세그먼트로 작동. 세그먼트 이름은 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/)에 페이지 없으면 접근 불가.

Catch-all Segments

다이나믹 세그먼트를 확장하여 경로의 여러 세그먼트를 포착하려면 폴더 이름 앞에 점 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/)에 페이지 없으면 접근 불가.

Optional Catch-all Segments

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 (❌ 불가)

Generating Static Params

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>;
}