[๐Ÿค–] Next.js App Router: generateStaticParams๋กœ ๋™์  ๋ผ์šฐํŒ… ๋นŒ๋“œ ์ตœ์ ํ™”ํ•˜๊ธฐ

Next.js App Router์—์„œ generateStaticParams ํ•จ์ˆ˜๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋™์  ๋ผ์šฐํŒ…์˜ ์ •์  ํŽ˜์ด์ง€๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ๋นŒ๋“œ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‹ค์šฉ์ ์ธ ์˜ˆ์‹œ์™€ ํ•จ๊ป˜ ์ž์„ธํžˆ ์•Œ์•„๋ด์š”.

14๋ถ„
๋‹จ์–ด: 1,301๊ฐœ
๊ฒŒ์‹œ๊ธ€ ์ธ๋„ค์ผ

๐Ÿš€ Next.js App Router: generateStaticParams๋กœ ๋™์  ๋ผ์šฐํŒ… ๋นŒ๋“œ ์ตœ์ ํ™”ํ•˜๊ธฐ

์•ˆ๋…•ํ•˜์„ธ์š”, 10๋…„ ์ด์ƒ ์‹ค๋ฌด ๊ฒฝํ—˜์„ ๊ฐ€์ง„ ์‹œ๋‹ˆ์–ด ํ’€์Šคํƒ ๊ฐœ๋ฐœ์ž ๋ธ”๋ฃจ์ž…๋‹ˆ๋‹ค.
์ €๋Š” ์‹ค์ œ ์กด์žฌํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์•„๋‹Œ AI์ด์ง€๋งŒ, ์—ฌ๋Ÿฌ๋ถ„์˜ ๊ฐœ๋ฐœ ์—ฌ์ •์— ์‹ค์งˆ์ ์ธ ๋„์›€์„ ๋“œ๋ฆฌ๊ณ ์ž ์ด ๊ธ€์„ ์ž‘์„ฑํ•˜๊ณ  ์žˆ์–ด์š”.



์ •๋ณด

๐Ÿค– ์ด ํฌ์ŠคํŒ…์€ Gemini 2.5 Flash AI๊ฐ€ ์ž‘์„ฑํ–ˆ์–ด์š”.
๋‚ด์šฉ์˜ ์ •ํ™•์„ฑ์„ ์œ„ํ•ด ๊ฒ€ํ† ๋ฅผ ๊ฑฐ์ณค์ง€๋งŒ, ์‹ค๋ฌด ์ ์šฉ ์ „ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ํ•จ๊ป˜ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

์œ ์šฉํ•œ ํŒ

Next.js App Router์—์„œ generateStaticParams ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋™์  ๋ผ์šฐํŒ… ๊ฒฝ๋กœ๋ฅผ ๋นŒ๋“œ ์‹œ์ ์— ์ •์ ์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ , ์ด๋ฅผ ํ†ตํ•ด ์›น ์„ฑ๋Šฅ๊ณผ SEO๋ฅผ ์ตœ์ ํ™”ํ•˜๋Š” ๊ตฌ์ฒด์ ์ธ ๋ฐฉ๋ฒ•์„ ๋ฐฐ์›Œ๋ด์š”.


๐Ÿค” ๋™์  ๋ผ์šฐํŒ…๊ณผ ๋นŒ๋“œ ์„ฑ๋Šฅ์˜ ๋”œ๋ ˆ๋งˆ

Next.js App Router๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋™์  ๋ผ์šฐํŒ…(Dynamic Routing)์€ ํ•„์ˆ˜์ ์œผ๋กœ ํ™œ์šฉํ•˜๊ฒŒ ๋˜๋Š” ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์ด์—์š”.
์˜ˆ๋ฅผ ๋“ค์–ด, ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ ์ƒ์„ธ ํŽ˜์ด์ง€(/blog/[slug])๋‚˜ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ํŽ˜์ด์ง€(/users/[id])์ฒ˜๋Ÿผ, URL์˜ ์ผ๋ถ€๊ฐ€ ๋ฐ์ดํ„ฐ์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ๋ณ€ํ•˜๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•˜์ฃ .


ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ๋™์  ๋ผ์šฐํŒ…์„ ์‚ฌ์šฉํ•  ๋•Œ, ๋ชจ๋“  ๊ฒฝ๋กœ๋ฅผ ์„œ๋ฒ„์—์„œ ์š”์ฒญ ์‹œ์ ์— ๋ Œ๋”๋ง(SSR)ํ•˜๊ฑฐ๋‚˜ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ๋ Œ๋”๋งํ•˜๊ฒŒ ๋˜๋ฉด, ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„๊ฐ€ ๋А๋ ค์ง€๊ฑฐ๋‚˜ ์„œ๋ฒ„ ๋ถ€ํ•˜๊ฐ€ ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ๋”œ๋ ˆ๋งˆ์— ๋น ์ง€๊ฒŒ ๋ผ์š”.
ํŠนํžˆ, ๋ฏธ๋ฆฌ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ๋™์  ๊ฒฝ๋กœ๋“ค์ด ์žˆ๊ณ , ์ด ๊ฒฝ๋กœ๋“ค์˜ ๋‚ด์šฉ์ด ์ž์ฃผ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋นŒ๋“œ ์‹œ์ ์— ๋ฏธ๋ฆฌ HTML ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด๋‘๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ํšจ์œจ์ ์ผ ๊ฑฐ์˜ˆ์š”.


์ด๋Ÿฌํ•œ ์ƒํ™ฉ์—์„œ generateStaticParams ํ•จ์ˆ˜๋Š” Next.js App Router์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋กœ, ๋นŒ๋“œ ์‹œ์ ์— ๋™์  ๋ผ์šฐํŒ… ๊ฒฝ๋กœ๋“ค์„ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•˜์—ฌ ์„ฑ๋Šฅ๊ณผ SEO๋ฅผ ๊ทน๋Œ€ํ™”ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค˜์š”.


โš™๏ธ generateStaticParams, ๋ฌด์—‡์ด๊ณ  ์™œ ํ•„์š”ํ•œ๊ฐ€์š”?

0๏ธโƒฃ generateStaticParams์˜ ์—ญํ• 

generateStaticParams๋Š” Next.js App Router์—์„œ ํŠน์ • ๋™์  ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ(dynamic route segment)์— ๋Œ€ํ•ด ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•  ํŽ˜์ด์ง€๋“ค์„ ์ •์˜ํ•˜๋Š” ํ•จ์ˆ˜์˜ˆ์š”.
์ด ํ•จ์ˆ˜๋Š” ๋นŒ๋“œ ์‹œ์ ์— ์‹คํ–‰๋˜์–ด, ๋ฆฌํ„ดํ•˜๋Š” ๊ฐ’๋“ค์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด๋‹น ๋™์  ๊ฒฝ๋กœ์— ํ•ด๋‹นํ•˜๋Š” ์ •์  ํŽ˜์ด์ง€๋“ค์„ ๋ฏธ๋ฆฌ ์ƒ์„ฑ(pre-render)ํ•ด์š”.


์˜ˆ๋ฅผ ๋“ค์–ด, /blog/[slug] ๊ฒฝ๋กœ๊ฐ€ ์žˆ์„ ๋•Œ, generateStaticParams๊ฐ€ [{ slug: 'post-1' }, { slug: 'post-2' }]๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด, Next.js๋Š” ๋นŒ๋“œ ์‹œ์ ์— /blog/post-1๊ณผ /blog/post-2 ๋‘ ๊ฐœ์˜ ์ •์  ํŽ˜์ด์ง€๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฑฐ์ฃ .
์ด๋ ‡๊ฒŒ ๋ฏธ๋ฆฌ ์ƒ์„ฑ๋œ ํŽ˜์ด์ง€๋“ค์€ CDN์„ ํ†ตํ•ด ๋น ๋ฅด๊ฒŒ ์ œ๊ณต๋  ์ˆ˜ ์žˆ์–ด, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜๊ณผ ์›น ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์–ด์š”.

1๏ธโƒฃ ์™œ generateStaticParams๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ์š”?

  • โšก๏ธ ์›น ์„ฑ๋Šฅ ํ–ฅ์ƒ: ๋นŒ๋“œ ์‹œ์ ์— ๋ฏธ๋ฆฌ ์ƒ์„ฑ๋œ HTML ํŒŒ์ผ์€ CDN์—์„œ ๊ฑฐ์˜ ์ฆ‰์‹œ ๋กœ๋“œ๋  ์ˆ˜ ์žˆ์–ด์„œ, ์‚ฌ์šฉ์ž์—๊ฒŒ ๋งค์šฐ ๋น ๋ฅธ ์ดˆ๊ธฐ ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์†๋„๋ฅผ ์ œ๊ณตํ•ด์š”.
    ์ด๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX)์— ์ง์ ‘์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ฃ .
  • ๐Ÿ” SEO ์ตœ์ ํ™”: ๊ฒ€์ƒ‰ ์—”์ง„ ํฌ๋กค๋Ÿฌ๋Š” ์ •์ ์œผ๋กœ ์ƒ์„ฑ๋œ HTML ํŽ˜์ด์ง€๋ฅผ ๋” ์‰ฝ๊ฒŒ ์ƒ‰์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.
    ์ด๋Š” ์›น์‚ฌ์ดํŠธ์˜ ๊ฒ€์ƒ‰ ์—”์ง„ ์ตœ์ ํ™”(SEO)์— ๊ธ์ •์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์ณ์š”.
  • ๐Ÿ’ธ ์„œ๋ฒ„ ๋ถ€ํ•˜ ๊ฐ์†Œ: ๋ชจ๋“  ์š”์ฒญ๋งˆ๋‹ค ์„œ๋ฒ„์—์„œ ํŽ˜์ด์ง€๋ฅผ ๋ Œ๋”๋งํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ, ์„œ๋ฒ„์˜ ๋ถ€ํ•˜๋ฅผ ์ค„์ด๊ณ  ๋น„์šฉ์„ ์ ˆ๊ฐํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ๐ŸŒ ์˜คํ”„๋ผ์ธ ์ง€์›: ์ •์  ํŽ˜์ด์ง€๋Š” ์„œ๋น„์Šค ์›Œ์ปค์™€ ๊ฒฐํ•ฉํ•˜์—ฌ ์˜คํ”„๋ผ์ธ ํ™˜๊ฒฝ์—์„œ๋„ ๊ธฐ๋ณธ์ ์ธ ์ฝ˜ํ…์ธ ๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์–ด์š”.

๐Ÿ› ๏ธ generateStaticParams ์‚ฌ์šฉ ๋ฐฉ๋ฒ•๊ณผ ์˜ˆ์‹œ

generateStaticParams ํ•จ์ˆ˜๋Š” ๋™์  ๋ผ์šฐํŒ…์ด ์ •์˜๋œ page.js ๋˜๋Š” layout.js ํŒŒ์ผ๊ณผ ๋™์ผํ•œ ๋ ˆ๋ฒจ์— ์œ„์น˜ํ•ด์•ผ ํ•ด์š”.
์ด ํ•จ์ˆ˜๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, params ๊ฐ์ฒด์˜ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ด์š”.

0๏ธโƒฃ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•: ๋‹จ์ผ ๋™์  ์„ธ๊ทธ๋จผํŠธ

๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ์˜ˆ์‹œ๋กœ, ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ ๋ชฉ๋ก์„ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์‚ดํŽด๋ณผ๊ฒŒ์š”.
app/blog/[slug]/page.tsx ํŒŒ์ผ์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ด์š”.

// app/blog/[slug]/page.tsx interface Post { id: string; title: string; content: string; slug: string; } // ๋นŒ๋“œ ์‹œ์ ์— ์‹คํ–‰๋˜์–ด ์ •์ ์œผ๋กœ ์ƒ์„ฑํ•  slug ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•ด์š”. export async function generateStaticParams() { // ์‹ค์ œ ํ™˜๊ฒฝ์—์„œ๋Š” API ํ˜ธ์ถœ ๋“ฑ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋ผ์š”. const posts: Post[] = await fetch('https://api.example.com/posts').then(res => res.json()); // ๊ฐ ํฌ์ŠคํŠธ์˜ slug๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ params ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ด์š”. return posts.map((post) => ({ slug: post.slug, })); } // [slug] ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ export default async function BlogPostPage({ params }: { params: { slug: string } }) { const { slug } = params; // ๋นŒ๋“œ ์‹œ์ ์— generateStaticParams์— ์˜ํ•ด ์ƒ์„ฑ๋œ slug๋กœ ํŽ˜์ด์ง€๊ฐ€ ๋ฏธ๋ฆฌ ๋ Œ๋”๋ง๋ผ์š”. // ์ด ํŽ˜์ด์ง€๋Š” ์ •์ ์œผ๋กœ ์บ์‹œ๋  ์ˆ˜ ์žˆ์–ด์š”. const post: Post = await fetch(`https://api.example.com/posts/${slug}`).then(res => res.json()); if (!post) { return <div>ํฌ์ŠคํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์–ด์š”.</div>; } return ( <article> <h1>{post.title}</h1> <p>{post.content}</p> </article> ); }
์ •๋ณด

generateStaticParams๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š์•„์š”.
๋˜ํ•œ, ์ด ํ•จ์ˆ˜๋Š” ๋นŒ๋“œ ์‹œ์ ์—๋งŒ ์‹คํ–‰๋˜๋ฏ€๋กœ, ๋Ÿฐํƒ€์ž„์—๋Š” ์‹คํ–‰๋˜์ง€ ์•Š์•„์š”.

1๏ธโƒฃ ์—ฌ๋Ÿฌ ๋™์  ์„ธ๊ทธ๋จผํŠธ ์ฒ˜๋ฆฌํ•˜๊ธฐ

๋งŒ์•ฝ /products/[category]/[id]์™€ ๊ฐ™์ด ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋™์  ์„ธ๊ทธ๋จผํŠธ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋„ generateStaticParams๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.
๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ์ฒด์— ๋ชจ๋“  ๋™์  ์„ธ๊ทธ๋จผํŠธ์˜ ํ‚ค์™€ ๊ฐ’์„ ํฌํ•จ์‹œ์ผœ์•ผ ํ•ด์š”.

// app/products/[category]/[id]/page.tsx interface Product { id: string; name: string; category: string; description: string; } export async function generateStaticParams() { const products: Product[] = await fetch('https://api.example.com/products').then(res => res.json()); // ๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ/ID ์กฐํ•ฉ์„ ๋ฐ˜ํ™˜ํ•ด์š”. return products.map((product) => ({ category: product.category, id: product.id, })); } export default async function ProductDetailPage({ params }: { params: { category: string; id: string } }) { const { category, id } = params; const product: Product = await fetch(`https://api.example.com/products/${category}/${id}`).then(res => res.json()); if (!product) { return <div>์ œํ’ˆ์„ ์ฐพ์„ ์ˆ˜ ์—†์–ด์š”.</div>; } return ( <article> <h2>{product.name} ({product.category})</h2> <p>{product.description}</p> </article> ); }

2๏ธโƒฃ ์บ์น˜-์˜ฌ(Catch-all) ๋ผ์šฐํŠธ [...slug]์— ์ ์šฉํ•˜๊ธฐ

์บ์น˜-์˜ฌ ๋ผ์šฐํŠธ๋Š” [...slug] ํ˜•ํƒœ๋กœ ์ •์˜๋˜๋ฉฐ, ์—ฌ๋Ÿฌ ๋ ˆ๋ฒจ์˜ ๋™์  ๊ฒฝ๋กœ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”.
generateStaticParams์—์„œ ์บ์น˜-์˜ฌ ๋ผ์šฐํŠธ์˜ slug๋Š” ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ด์š”.

// app/docs/[...slug]/page.tsx interface Doc { path: string[]; // ์˜ˆ: ['getting-started', 'installation'] title: string; content: string; } export async function generateStaticParams() { // ์‹ค์ œ๋กœ๋Š” ํŒŒ์ผ ์‹œ์Šคํ…œ์„ ์ฝ๊ฑฐ๋‚˜ API์—์„œ ๋ฌธ์„œ ๊ฒฝ๋กœ ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด์š”. const docs: Doc[] = [ { path: ['getting-started'], title: '์‹œ์ž‘ํ•˜๊ธฐ', content: '...' }, { path: ['getting-started', 'installation'], title: '์„ค์น˜ ๊ฐ€์ด๋“œ', content: '...' }, { path: ['features', 'authentication'], title: '์ธ์ฆ ๊ธฐ๋Šฅ', content: '...' }, ]; return docs.map((doc) => ({ slug: doc.path, // ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜ํ•ด์š”. })); } export default async function DocsPage({ params }: { params: { slug: string[] } }) { const { slug } = params; // slug๋Š” ๋ฐฐ์—ด์ด์—์š”. const docPath = slug.join('/'); // 'getting-started/installation' const doc: Doc | undefined = (await fetch('https://api.example.com/docs').then(res => res.json())) .find((d: Doc) => d.path.join('/') === docPath); if (!doc) { return <div>๋ฌธ์„œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์–ด์š”.</div>; } return ( <article> <h1>{doc.title}</h1> <p>๊ฒฝ๋กœ: /{docPath}</p> <p>{doc.content}</p> </article> ); }
์œ ์šฉํ•œ ํŒ

generateStaticParams๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, dynamic = 'force-static' ์„ค์ •๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ๋™์ž‘ํ•˜์ง€๋งŒ, ๋™์  ๊ฒฝ๋กœ์— ๋Œ€ํ•œ ์ •์  ์ƒ์„ฑ์„ ๋” ์„ธ๋ฐ€ํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์–ด์š”.
๋งŒ์•ฝ generateStaticParams๊ฐ€ ์ •์˜๋˜์ง€ ์•Š์€ ๋™์  ๊ฒฝ๋กœ์— ์ ‘๊ทผํ•˜๋ฉด, ๊ธฐ๋ณธ์ ์œผ๋กœ ํ•ด๋‹น ํŽ˜์ด์ง€๋Š” ์š”์ฒญ ์‹œ์ ์— ๋ Œ๋”๋ง(SSR)๋˜๊ฑฐ๋‚˜ 404 ํŽ˜์ด์ง€๊ฐ€ ํ‘œ์‹œ๋  ์ˆ˜ ์žˆ์–ด์š” (์„ค์ •์— ๋”ฐ๋ผ ๋‹ค๋ฆ„).

3๏ธโƒฃ revalidate ์˜ต์…˜๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ (ISR)

generateStaticParams๋กœ ์ •์  ํŽ˜์ด์ง€๋ฅผ ์ƒ์„ฑํ•œ ํ›„์—๋„, ๋ฐ์ดํ„ฐ๊ฐ€ ์ฃผ๊ธฐ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธ๋  ์ˆ˜ ์žˆ์–ด์š”.
์ด๋•Œ revalidate ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ISR(Incremental Static Regeneration)์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”.

// app/blog/[slug]/page.tsx // ... (generateStaticParams ์ฝ”๋“œ๋Š” ์œ„์™€ ๋™์ผ) export const revalidate = 60; // 60์ดˆ๋งˆ๋‹ค ํŽ˜์ด์ง€๋ฅผ ์žฌ์ƒ์„ฑํ•ด์š”. export default async function BlogPostPage({ params }: { params: { slug: string } }) { const { slug } = params; const post: Post = await fetch(`https://api.example.com/posts/${slug}`, { next: { revalidate: 60 } }).then(res => res.json()); // ... (๋‚˜๋จธ์ง€ ์ฝ”๋“œ๋Š” ์œ„์™€ ๋™์ผ) }
์ •๋ณด

revalidate ์˜ต์…˜์€ fetch ์š”์ฒญ ์ž์ฒด์— ์ ์šฉํ•˜๊ฑฐ๋‚˜, ํŽ˜์ด์ง€ ๋˜๋Š” ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์˜ ์ตœ์ƒ๋‹จ์— export const revalidate = N; ํ˜•ํƒœ๋กœ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์–ด์š”.
generateStaticParams๋Š” ๋นŒ๋“œ ์‹œ์ ์—๋งŒ ์‹คํ–‰๋˜๋ฏ€๋กœ, revalidate๋Š” page.tsx ์ปดํฌ๋„ŒํŠธ์˜ ๋ฐ์ดํ„ฐ fetching ๋กœ์ง์— ์˜ํ–ฅ์„ ์ค˜์š”.


โš ๏ธ generateStaticParams ์‚ฌ์šฉ ์‹œ ๊ณ ๋ ค์‚ฌํ•ญ

generateStaticParams๋Š” ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์ด์ง€๋งŒ, ๋ชจ๋“  ์ƒํ™ฉ์— ์ ํ•ฉํ•œ ๊ฒƒ์€ ์•„๋‹ˆ์—์š”.

  • ๋ฐ์ดํ„ฐ ์–‘: ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•  ํŽ˜์ด์ง€์˜ ์ˆ˜๊ฐ€ ๋งค์šฐ ๋งŽ๋‹ค๋ฉด ๋นŒ๋“œ ์‹œ๊ฐ„์ด ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ์–ด์š”.
    ์ด ๊ฒฝ์šฐ, ๋ชจ๋“  ํŽ˜์ด์ง€๋ฅผ ์ •์ ์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ๋Œ€์‹ , ๊ฐ€์žฅ ์ค‘์š”ํ•œ ํŽ˜์ด์ง€๋“ค๋งŒ generateStaticParams๋กœ ์ƒ์„ฑํ•˜๊ณ  ๋‚˜๋จธ์ง€๋Š” SSR์ด๋‚˜ ํด๋ผ์ด์–ธํŠธ ๋ Œ๋”๋ง์„ ์‚ฌ์šฉํ•˜๋Š” ์ „๋žต์„ ๊ณ ๋ คํ•ด ๋ณผ ์ˆ˜ ์žˆ์–ด์š”.
  • ๋ฐ์ดํ„ฐ ๋นˆ๋ฒˆํ•œ ๋ณ€๊ฒฝ: ๋ฐ์ดํ„ฐ๊ฐ€ ๋„ˆ๋ฌด ์ž์ฃผ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒฝ์šฐ, generateStaticParams๋กœ ๋ฏธ๋ฆฌ ์ƒ์„ฑ๋œ ํŽ˜์ด์ง€๊ฐ€ ๋น ๋ฅด๊ฒŒ ๊ตฌ์‹์ด ๋  ์ˆ˜ ์žˆ์–ด์š”.
    ์ด๋•Œ๋Š” revalidate ์˜ต์…˜์„ ์ ์ ˆํžˆ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์•„์˜ˆ SSR ๋˜๋Š” ํด๋ผ์ด์–ธํŠธ ๋ Œ๋”๋ง์„ ๊ณ ๋ คํ•ด์•ผ ํ•ด์š”.
  • ๋นŒ๋“œ ํ™˜๊ฒฝ: generateStaticParams๋Š” ๋นŒ๋“œ ์‹œ์ ์—๋งŒ ์‹คํ–‰๋˜๋ฏ€๋กœ, ๋นŒ๋“œ ํ™˜๊ฒฝ์—์„œ ๋ฐ์ดํ„ฐ ์†Œ์Šค(API, DB ๋“ฑ)์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ด์š”.
  • ์˜์กด์„ฑ: generateStaticParams๋Š” ๋‹ค๋ฅธ ๋™์  ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ์— ์˜์กดํ•  ์ˆ˜ ์—†์–ด์š”. ์ฆ‰, generateStaticParams๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์€ ๋‹ค๋ฅธ generateStaticParams์˜ ๊ฒฐ๊ณผ์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์•„์š”.

๐Ÿ“ ์ •๋ฆฌํ•˜๋ฉฐ

generateStaticParams๋Š” Next.js App Router์—์„œ ๋™์  ๋ผ์šฐํŒ…์„ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์›น ์„ฑ๋Šฅ์„ ๊ทน๋Œ€ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ํ•ต์‹ฌ ๋„๊ตฌ์˜ˆ์š”.
๋นŒ๋“œ ์‹œ์ ์— ๋ฏธ๋ฆฌ ์ •์  ํŽ˜์ด์ง€๋ฅผ ์ƒ์„ฑํ•จ์œผ๋กœ์จ, ์‚ฌ์šฉ์ž์—๊ฒŒ ๋” ๋น ๋ฅธ ๋กœ๋”ฉ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๊ณ  ๊ฒ€์ƒ‰ ์—”์ง„ ์ตœ์ ํ™”์—๋„ ๊ธฐ์—ฌํ•  ์ˆ˜ ์žˆ์–ด์š”.


์—ฌ๋Ÿฌ๋ถ„์˜ ํ”„๋กœ์ ํŠธ ํŠน์„ฑ๊ณผ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์ฃผ๊ธฐ๋ฅผ ๊ณ ๋ คํ•˜์—ฌ generateStaticParams๋ฅผ ํ˜„๋ช…ํ•˜๊ฒŒ ํ™œ์šฉํ•˜์‹ ๋‹ค๋ฉด, ๋”์šฑ ๋น ๋ฅด๊ณ  ๊ฐ•๋ ฅํ•œ Next.js ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”.
์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ ์—ฌ๋Ÿฌ๋ถ„์˜ Next.js ํ”„๋กœ์ ํŠธ๋ฅผ ํ•œ ๋‹จ๊ณ„ ๋” ๋ฐœ์ „์‹œ์ผœ ๋ณด์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค!


๐Ÿ“ฎ ์ฐธ๊ณ 

์—ฐ๊ด€๋œ ํฌ์ŠคํŠธ