Intro
정확한 세그먼트 이름을 사전에 알지 못하고 동적인 데이터로 경로를 생성하려는 경우, request를 할 때나 빌드할 때 미리 렌더링되는 동적 세그먼트를 사용할 수 있다.
TOC
Convention
동적 세그먼트는 폴더 이름을 대괄호([]
) 로 묶어 만들 수 있습니다: [폴더 이름]
. (예: [id]
또는 [slug]
).
Group은 소괄호, Dynamic Routes는 대괄호
동적 세그먼트는 layout
, page
, route
, generateMetadata
functions에 params
로 전달된다.
Example
이 부분은 page 디렉토리 방식과 유사하나, 예시를 보면서 이해하는데 도움이 된다.
export default function Page({ params }: { params: { slug: string } }) {
return <div>My Post: {params.slug}</div>
}
위와 같은 경로로 page
파일을 만들었다면 [slug]
라고 이름을 지은 폴더명에 따라 Component의 params
에 slug
라는 이름으로 값이 넘어오게 된다.
Route | Example | URL 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' } |
세그먼트에 대한 params를 생성하는 방법을 알아보려면 generateStaticParams()
페이지를 참조하면 된다. Next.js 13으로 블로그를 만들때 유용한 함수다.
Generating Static Params
generateStaticParams
함수는 동적 경로 세그먼트와 함께 사용하여 request시 on-demand가 아닌 빌드 시점에 경로를 정적으로 생성할 수 있다(블로그를 만들때 사용하였다.).
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
return posts.map((post) => ({
slug: post.slug,
}))
}
generateStaticParams 함수의 가장 큰 장점은 데이터 검색이다. fetch
요청을 사용하여 generateStaticParams
함수 내에서 콘텐츠를 가져오면 요청이 자동 중복제거한다. 즉, 여러 generateStaticParams
, Layouts 및 Pages에 걸쳐 동일한 인수를 가진 fetch 요청은 한 번만 수행되므로 빌드 시간이 단축됩니다.
Catch-all Segments
동적 세그먼트는 괄호 안에 줄임표를 추가하여 [...폴더이름]
안에 모든 후속 세그먼트(catch-all subsequent)를 포함하도록 확장할 수 있다.
예를 들어 app/shop/[...slug]/page.js
는 /shop/clothes
뿐만 아니라 /shop/clothes/tops
, /shop/clothes/tops/t-shirts
도 동일한 파일을 사용한다.
Route | Example | URL params |
---|---|---|
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'] } |
표에서 확인할 수 있듯이 값이 String[]
으로 넘어온다는 것을 명심해야한다.
Optional Catch-all Segments
Catch-all Segments는 매개 변수를 이중 대괄호 안에 포함하여 선택 사항으로 만들 수 있다: [[...폴더이름]]
.
예를 들어, app/shop/[[...slug]]/page.js
파일이 있으면, /shop
, /shop/clothes
, /shop/clothes/tops
, /shop/clothes/tops/t-shirts
모두에서 사용되는 매칭이 되는 것이다.
catch-all과 optional catch-all segments의 차이점은 optional인데, 이는 /shop
뒤에 아무런 parameter가 없더라도 optional catch-all segments는 작동한다는 차이점이 있다.
개인적으로는 글만 보고서 차이점이 와닿지 않았다. 그러나 폴더구조로 살펴보면 이해가 잘된다. app/shop/[[...slug]]/page.js
이 있고 app/shop/page.js
파일이 없다면 catch-all에서는 Not Found가 나올 것이다. 그러나 optional catch-all에서는 app/shop/[[...slug]]/page.js
파일이 동작해서 나오게 된다.
Route | Example | URL 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'] } |