icon
4장 : 라우팅 심화

동적 라우트 생성

이전 장에서 우리는 정적인 URL 경로를 가진 페이지를 만들고 연결하는 방법을 배웠습니다. 하지만 실제 웹 애플리케이션에서는 URL의 일부가 사용자나 데이터에 따라 동적으로 변해야 하는 경우가 훨씬 많습니다. 예를 들어, 블로그 게시물 상세 페이지(/blog/nextjs-basics), 상품 상세 페이지(/products/luxury-watch-xyz), 사용자 프로필 페이지(/users/alice) 등이 그 예시입니다.

Next.js App Router는 이러한 요구사항을 충족시키기 위해 동적 라우트(Dynamic Routes) 기능을 제공합니다. 이 절에서는 동적 라우트를 생성하고, URL에서 동적인 값을 추출하여 페이지 컴포넌트에서 사용하는 방법에 대해 자세히 알아보겠습니다.


동적 라우트의 개념과 필요성

정적 라우트가 /about이나 /dashboard/settings처럼 고정된 URL을 가진다면, 동적 라우트는 URL의 특정 부분이 변수처럼 작동합니다.

  • 블로그 게시물: /blog/my-first-post, /blog/nextjs-deep-dive
  • 상품 상세: /products/12345, /products/67890
  • 사용자 프로필: /users/john-doe, /users/jane-smith

이러한 URL들은 /blog/, /products/, /users/는 고정되어 있지만, 그 뒤의 my-first-post, 12345, john-doe와 같은 값은 각 항목에 따라 달라집니다. Next.js는 이 동적인 부분을 감지하고, 해당 값을 페이지 컴포넌트로 전달하여 우리가 각기 다른 콘텐츠를 렌더링할 수 있도록 돕습니다.


동적 라우트 생성하기: 대괄호 [] 사용

Next.js App Router에서 동적 라우트 세그먼트를 정의하려면, 폴더 이름을 대괄호([]) 로 감쌉니다. 이 대괄호 안의 이름이 파라미터(Parameter)의 키(key)가 됩니다.

예를 들어, 블로그 게시물 상세 페이지를 만든다고 가정해 봅시다.

  1. src/app/blog 폴더 생성: 먼저 src/app 안에 blog 폴더를 생성합니다. (만약 없다면)

    src/
    └── app/
        ├── blog/  <- 여기에 blog 폴더 생성
        ├── ...
  2. [slug] 폴더 생성: src/app/blog 안에 [slug]라는 이름의 폴더를 생성합니다. 여기서 slug는 동적인 값이 들어갈 파라미터의 이름이 됩니다.

    src/
    └── app/
        ├── blog/
        │   └── [slug]/  <- 여기에 [slug] 폴더 생성
        ├── ...
  3. page.tsx 파일 생성: src/app/blog/[slug] 폴더 안에 page.tsx 파일을 생성합니다.

    src/
    └── app/
        ├── blog/
        │   └── [slug]/
        │       └── page.tsx  <- 여기에 page.tsx 파일 생성
        ├── ...
  4. src/app/blog/[slug]/page.tsx 파일 내용 작성: 이 page.tsx 컴포넌트는 params라는 prop을 통해 URL에서 추출된 동적인 값을 전달받습니다.

    src/app/blog/[slug]/page.tsx
    // src/app/blog/[slug]/page.tsx
    
    interface BlogDetailPageProps {
      params: {
        slug: string; // [slug] 폴더 이름과 일치해야 합니다.
      };
    }
    
    // 서버 컴포넌트로 작동하며, params를 prop으로 받습니다.
    export default function BlogDetailPage({ params }: BlogDetailPageProps) {
      const { slug } = params; // URL에서 slug 값을 추출
    
      // 실제 애플리케이션에서는 이 slug 값을 사용하여
      // 데이터베이스나 API에서 해당 게시물의 내용을 불러올 것입니다.
      // 예: const post = await getPostBySlug(slug);
    
      return (
        <div>
          <h1>블로그 게시물: {slug}</h1>
          <p>
            현재 보고 계신 게시물의 식별자(slug)는 <strong>{slug}</strong> 입니다.
          </p>
          <p>여기에 실제 게시물 내용이 표시됩니다.</p>
        </div>
      );
    }

실습: 개발 서버가 실행 중인 상태에서 브라우저를 열고 다음 URL로 접속해 보세요.

  • http://localhost:3000/blog/my-first-post
  • http://localhost:3000/blog/nextjs-deep-dive
  • http://localhost:3000/blog/welcome-to-my-blog

각 URL에 따라 페이지 제목과 내용에 slug 값이 다르게 표시되는 것을 확인할 수 있습니다.


다중 동적 라우트 세그먼트

하나의 라우트에 여러 개의 동적 세그먼트를 포함할 수도 있습니다. 예를 들어, 특정 카테고리 내의 블로그 게시물을 나타내는 /blog/[category]/[slug]와 같은 경로를 만들 수 있습니다.

  1. src/app/blog/[category]/[slug]/page.tsx 구조 만들기

    src/
    └── app/
        └── blog/
            └── [category]/  <- [category] 폴더
                └── [slug]/    <- [slug] 폴더
                    └── page.tsx <- 페이지 파일
  2. src/app/blog/[category]/[slug]/page.tsx 파일 내용 작성

    src/app/blog/[category]/[slug]/page.tsx
    // src/app/blog/[category]/[slug]/page.tsx
    
    interface CategoryBlogDetailPageProps {
      params: {
        category: string; // [category] 폴더 이름과 일치
        slug: string;     // [slug] 폴더 이름과 일치
      };
    }
    
    export default function CategoryBlogDetailPage({ params }: CategoryBlogDetailPageProps) {
      const { category, slug } = params;
    
      return (
        <div>
          <h1>카테고리: {category}</h1>
          <h2>게시물: {slug}</h2>
          <p>
            이 게시물은 <strong>{category}</strong> 카테고리에 속하며, 식별자는 <strong>{slug}</strong> 입니다.
          </p>
        </div>
      );
    }

실습: 이제 다음 URL로 접속해 보세요.

  • http://localhost:3000/blog/web-dev/understanding-react
  • http://localhost:3000/blog/frontend/nextjs-features

URL의 categoryslug 부분이 정확히 추출되어 페이지에 표시되는 것을 볼 수 있습니다.


generateStaticParams 함수

동적 라우트를 가진 페이지를 정적으로 생성(Static Site Generation, SSG)해야 할 때, Next.js는 빌드 시점에 어떤 params 값을 가지고 페이지를 생성할지 알아야 합니다. 이를 위해 generateStaticParams 함수를 사용합니다. 이 함수는 서버 컴포넌트와 동일한 파일에 정의할 수 있습니다.

src/app/blog/[slug]/page.tsx
// src/app/blog/[slug]/page.tsx (앞서 만든 파일에 추가)

// ... (BlogDetailPage 컴포넌트 코드) ...

// SSG를 위해 빌드 시 어떤 slug 값을 생성할지 명시
export async function generateStaticParams() {
  const posts = [
    { slug: 'my-first-post' },
    { slug: 'nextjs-deep-dive' },
    { slug: 'understanding-server-components' },
  ];

  // 반환 값은 { slug: '...' } 형태의 객체 배열이어야 합니다.
  return posts;
}

generateStaticParams 함수가 정의되면, Next.js는 빌드 시 이 함수가 반환하는 slug 값들을 사용하여 미리 HTML 페이지를 생성합니다. 이는 첫 로딩 속도를 매우 빠르게 하고, CDN을 통해 콘텐츠를 효율적으로 제공할 수 있게 합니다.

동적 라우트는 Next.js의 강력한 기능 중 하나로, 실제 웹 애플리케이션의 다양한 요구사항을 충족시키는 데 필수적입니다. 이 개념을 잘 이해하고 활용한다면, 유연하고 확장 가능한 웹 서비스를 구축할 수 있을 것입니다.