Next.js 기초 입문 10회차: 동적 라우팅 (Dynamic Routes) 심층 분석

📚
제10주차 학습 목표

동적 라우팅 (Dynamic Routes)

URL 파라미터를 사용하여 동적인 페이지를 생성하는 방법을 익힙니다.

Next.js 기초 입문 10회차: 동적 라우팅 (Dynamic Routes)

안녕하세요, Next.js 기초 입문 학습자 여러분! 10회차에서는 웹 애플리케이션의 핵심 기능 중 하나인 동적 라우팅(Dynamic Routes)에 대해 학습합니다. 사용자 요청에 따라 다양한 콘텐츠를 유연하게 보여줄 수 있는 강력한 기능이죠. 이번 시간을 통해 동적 라우팅의 원리와 실제 구현 방법을 완벽하게 마스터하시길 바랍니다.

📌 이번 회차 학습 목표

  • URL 파라미터를 사용하여 동적인 페이지를 생성하는 원리를 이해할 수 있습니다.
  • Next.js에서 동적 라우팅을 설정하고 활용하는 방법을 실습할 수 있습니다.
  • [slug].js와 같은 파일명 규칙의 의미를 설명할 수 있습니다.
  • 동적 라우팅 페이지에서 데이터를 불러오는 다양한 전략(SSG, SSR)을 적용할 수 있습니다.
  • 동적 라우팅 구현 시 발생할 수 있는 일반적인 문제점과 해결책을 파악할 수 있습니다.

📝 개념 설명: 동적 라우팅의 이해

정적인 페이지는 미리 정해진 URL에 따라 고정된 콘텐츠를 제공합니다. 그러나 블로그 게시물, 상품 상세 페이지, 사용자 프로필 등은 수많은 항목을 가지고 있으며, 각 항목마다 개별적인 페이지를 만들어 관리하는 것은 비효율적입니다. 이때 동적 라우팅이 필요합니다.

동적 라우팅이란?

동적 라우팅(Dynamic Routes)은 URL의 특정 부분이 변할 때마다 다른 콘텐츠를 보여주는 라우팅 방식입니다. 예를 들어, /posts/1, /posts/2, /posts/hello-world와 같이 /posts/ 뒤의 값이 달라질 때마다 해당 값에 맞는 게시물 내용을 보여주는 것이죠. Next.js에서는 파일 시스템 기반 라우팅과 결합하여 매우 직관적으로 동적 라우팅을 구현할 수 있습니다.

URL 파라미터와 Slug

동적 라우팅에서 변하는 URL 부분을 URL 파라미터라고 합니다. Next.js에서는 이 파라미터를 slug라고도 부르며, 파일명에 대괄호([])를 사용하여 정의합니다.

  • 예시: pages/posts/[id].js
  • 이 경우, /posts/1 또는 /posts/abc와 같은 URL로 접근하면 [id].js 파일이 렌더링되며, id 값(1 또는 abc)을 페이지 컴포넌트에서 사용할 수 있게 됩니다.
📌 핵심 포인트: 정적 vs. 동적 라우팅
🔑
정적 라우팅
미리 정해진 고정된 URL에 따라 페이지를 제공합니다. (예: /about, /contact)
🔑
동적 라우팅
URL의 특정 부분이 변할 때마다 다른 콘텐츠를 보여줍니다. (예: /posts/[id], /users/[username])

💡 예제 & 실습: 블로그 게시물 상세 페이지 구현

이제 실제 Next.js 프로젝트에서 동적 라우팅을 구현하는 과정을 단계별로 살펴보겠습니다. 블로그 게시물 목록에서 특정 게시물을 클릭하면 해당 게시물의 상세 내용을 보여주는 페이지를 만들어봅시다.

1단계: 프로젝트 설정 및 게시물 목록 페이지 생성

먼저 Next.js 프로젝트를 생성하고, 게시물 목록을 보여줄 페이지를 만듭니다.


# Next.js 프로젝트 생성 (이미 있다면 생략)
npx create-next-app my-blog
cd my-blog

# pages/index.js (게시물 목록 페이지)

// pages/index.js
import Link from 'next/link';

const posts = [
  { id: '1', title: '첫 번째 게시물', content: '이것은 첫 번째 게시물의 내용입니다.' },
  { id: '2', title: '두 번째 게시물', content: '이것은 두 번째 게시물의 내용입니다.' },
  { id: 'hello-nextjs', title: 'Next.js 시작하기', content: 'Next.js와 함께 웹 개발을 시작해봅시다.' },
];

export default function HomePage() {
  return (
    <div>
      <h1>블로그 게시물 목록</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            <Link href={`/posts/${post.id}`}>
              <a>{post.title}</a>
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

해설: pages/index.js는 블로그의 메인 페이지 역할을 합니다. posts 배열에 가상의 게시물 데이터를 정의하고, 각 게시물의 id를 사용하여 동적 라우팅 링크를 생성합니다. <Link href={`/posts/${post.id}`} /> 부분이 핵심이며, 이는 /posts/1, /posts/2 등의 URL로 연결됩니다.

2단계: 동적 라우팅 페이지 생성

이제 /posts/[id] 경로에 해당하는 동적 페이지를 생성합니다. pages/posts 디렉토리 안에 [id].js 파일을 만듭니다.


# pages/posts/[id].js

// pages/posts/[id].js
import { useRouter } from 'next/router';

const postsData = [
  { id: '1', title: '첫 번째 게시물', content: '이것은 첫 번째 게시물의 내용입니다.' },
  { id: '2', title: '두 번째 게시물', content: '이것은 두 번째 게시물의 내용입니다.' },
  { id: 'hello-nextjs', title: 'Next.js 시작하기', content: 'Next.js와 함께 웹 개발을 시작해봅시다.' },
];

export default function PostDetail() {
  const router = useRouter();
  const { id } = router.query; // URL 파라미터 'id'를 가져옵니다.

  const post = postsData.find((p) => p.id === id);

  if (!post) {
    return <div>게시물을 찾을 수 없습니다.</div>;
  }

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
      <button onClick={() => router.back()}>목록으로 돌아가기</button>
    </div>
  );
}

해설: pages/posts/[id].js 파일은 /posts/ 경로 뒤에 어떤 값이 오든 이 페이지를 렌더링하도록 합니다. useRouter 훅을 사용하여 router.query 객체에서 URL 파라미터 id를 추출합니다. 이 id 값을 기반으로 postsData 배열에서 해당 게시물을 찾아 화면에 표시합니다.

3단계: 데이터 페칭 전략 (SSG, SSR)

실제 애플리케이션에서는 데이터가 외부 API나 데이터베이스에서 오게 됩니다. Next.js는 동적 라우팅 페이지에서 데이터를 가져오는 다양한 방법을 제공합니다.

정적 생성 (Static Site Generation, SSG) with getStaticPaths and getStaticProps

빌드 시점에 모든 동적 경로에 대한 페이지를 미리 생성하는 방식입니다. 블로그 게시물처럼 자주 변경되지 않는 콘텐츠에 적합합니다.


// pages/posts/[id].js (SSG 적용)
import { useRouter } from 'next/router';

const postsData = [
  { id: '1', title: '첫 번째 게시물', content: '이것은 첫 번째 게시물의 내용입니다.' },
  { id: '2', title: '두 번째 게시물', content: '이것은 두 번째 게시물의 내용입니다.' },
  { id: 'hello-nextjs', title: 'Next.js 시작하기', content: 'Next.js와 함께 웹 개발을 시작해봅시다.' },
];

export default function PostDetail({ post }) {
  const router = useRouter();

  if (router.isFallback) {
    return <div>로딩 중...</div>; // fallback: true일 때 사용
  }

  if (!post) {
    return <div>게시물을 찾을 수 없습니다.</div>;
  }

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
      <button onClick={() => router.back()}>목록으로 돌아가기</button>
    </div>
  );
}

// 빌드 시점에 생성할 모든 동적 경로를 정의합니다.
export async function getStaticPaths() {
  const paths = postsData.map((post) => ({
    params: { id: post.id },
  }));

  return { paths, fallback: false }; // fallback: false는 정의되지 않은 경로는 404 처리
}

// 각 경로에 필요한 데이터를 빌드 시점에 가져옵니다.
export async function getStaticProps({ params }) {
  const post = postsData.find((p) => p.id === params.id);

  if (!post) {
    return { notFound: true }; // 게시물이 없으면 404 페이지 반환
  }

  return { props: { post } };
}
해설:
  • getStaticPaths: Next.js에게 빌드 시점에 어떤 id 값으로 페이지를 미리 생성할지 알려줍니다. 여기서는 postsData의 모든 id를 기반으로 경로를 생성합니다. fallback: falsegetStaticPaths에 정의되지 않은 경로로 접근 시 404 페이지를 보여주도록 합니다.
  • getStaticProps: 각 경로(id)에 해당하는 데이터를 가져와 페이지 컴포넌트의 props로 전달합니다. 빌드 시점에 한 번만 실행되므로 성능이 매우 우수합니다.
  • router.isFallback: fallback: true로 설정했을 때, 아직 생성되지 않은 페이지에 접근하면 isFallbacktrue가 되어 로딩 상태를 보여줄 수 있습니다.
서버 사이드 렌더링 (Server-Side Rendering, SSR) with getServerSideProps

요청이 들어올 때마다 서버에서 데이터를 가져와 페이지를 렌더링하는 방식입니다. 실시간 데이터나 사용자별로 다른 콘텐츠를 보여줘야 할 때 유용합니다.


// pages/posts/[id].js (SSR 적용)
import { useRouter } from 'next/router';

const postsData = [
  { id: '1', title: '첫 번째 게시물', content: '이것은 첫 번째 게시물의 내용입니다.' },
  { id: '2', title: '두 번째 게시물', content: '이것은 두 번째 게시물의 내용입니다.' },
  { id: 'hello-nextjs', title: 'Next.js 시작하기', content: 'Next.js와 함께 웹 개발을 시작해봅시다.' },
];

export default function PostDetail({ post }) {
  const router = useRouter();

  if (!post) {
    return <div>게시물을 찾을 수 없습니다.</div>;
  }

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
      <button onClick={() => router.back()}>목록으로 돌아가기</button>
    </div>
  );
}

// 요청이 들어올 때마다 서버에서 데이터를 가져옵니다.
export async function getServerSideProps(context) {
  const { id } = context.query; // context.query에서 URL 파라미터 'id'를 가져옵니다.

  // 실제 API 호출을 시뮬레이션
  const post = postsData.find((p) => p.id === id);

  if (!post) {
    return { notFound: true };
  }

  return { props: { post } };
}
해설:
  • getServerSideProps: 클라이언트의 요청이 들어올 때마다 서버에서 실행됩니다. context.query를 통해 URL 파라미터 id에 접근할 수 있습니다. 이 함수 내에서 데이터를 가져와 페이지 컴포넌트의 props로 전달합니다.
🔄 동적 라우팅 데이터 흐름 (SSG vs. SSR)
SSG (getStaticPaths/Props)
1. 빌드 시점
2. getStaticPaths 실행 (경로 정의)
3. 각 경로별 getStaticProps 실행 (데이터 페칭)
4. HTML 파일 생성
5. 사용자 요청 시 미리 생성된 HTML 제공
SSR (getServerSideProps)
1. 사용자 요청 시점
2. getServerSideProps 실행 (데이터 페칭)
3. 서버에서 HTML 생성
4. 생성된 HTML 사용자에게 제공

⚠️ 자주 틀리는 것 / 주의사항

1. 대괄호([]) 사용 오류

동적 라우팅 파일명은 반드시 대괄호로 감싸야 합니다. pages/posts/id.js는 정적 페이지로 인식되며, /posts/id 경로에만 반응합니다. pages/posts/[id].js가 올바른 동적 라우팅 파일명입니다.

2. useRouter().query의 초기값

클라이언트 측에서 useRouter().query는 페이지가 처음 렌더링될 때 빈 객체일 수 있습니다. 특히 SSG 페이지에서 클라이언트 사이드 네비게이션으로 접근할 때 발생할 수 있습니다. 이 경우, 데이터가 없는 상태를 처리하는 로직(예: if (!post) return <div>로딩 중...</div>)이 필요할 수 있습니다.

3. getStaticPathsfallback 옵션 이해

  • fallback: false: getStaticPaths에 정의되지 않은 모든 경로는 404 페이지를 반환합니다.
  • fallback: true: getStaticPaths에 정의되지 않은 경로로 접근 시, Next.js는 해당 페이지를 즉시 404로 처리하지 않고, 백그라운드에서 페이지를 생성합니다. 이 동안 router.isFallbacktrue가 되어 로딩 UI를 보여줄 수 있습니다. 페이지 생성이 완료되면 캐시되고, 다음 요청부터는 미리 생성된 페이지를 제공합니다.
  • fallback: 'blocking': fallback: true와 유사하지만, 페이지가 생성될 때까지 클라이언트 요청을 블록합니다. 즉, 로딩 UI 없이 바로 완성된 페이지를 받게 됩니다.

각 옵션의 동작 방식을 정확히 이해하고 상황에 맞게 선택하는 것이 중요합니다.

4. 중첩 동적 라우팅

pages/users/[id]/posts/[postId].js와 같이 여러 개의 동적 세그먼트를 가질 수 있습니다. 이 경우 router.query{ id: '...', postId: '...' }와 같은 객체를 반환합니다.

✅ 이번 회차 핵심 정리
  • 동적 라우팅(Dynamic Routes)은 URL 파라미터를 사용하여 유연한 페이지 생성을 가능하게 합니다.
  • Next.js에서는 파일명에 대괄호([])를 사용하여 동적 라우팅을 정의하며, 이를 slug라고 부릅니다. (예: pages/posts/[id].js)
  • 페이지 컴포넌트 내에서 useRouter().query를 통해 URL 파라미터 값에 접근할 수 있습니다.
  • SSG (정적 생성)를 위해서는 getStaticPaths로 빌드 시 생성할 경로를 정의하고, getStaticProps로 각 경로의 데이터를 가져옵니다.
  • SSR (서버 사이드 렌더링)을 위해서는 getServerSideProps를 사용하여 요청 시점에 데이터를 가져옵니다.
  • getStaticPathsfallback 옵션(false, true, 'blocking')은 동적 경로 처리 방식에 중요한 영향을 미치므로 정확히 이해해야 합니다.

🔗 다음 회차 예고

이번 회차에서는 동적 라우팅을 통해 개별 페이지를 생성하고 데이터를 가져오는 방법을 학습했습니다. 다음 11회차에서는 Next.js의 API Routes에 대해深入적으로 다룰 예정입니다. 클라이언트에서 직접 데이터베이스에 접근하는 대신, Next.js 애플리케이션 내에서 서버리스 함수 형태로 API를 생성하고 활용하는 방법을 익히게 될 것입니다. 이는 프론트엔드와 백엔드의 경계를 허물고 풀스택 애플리케이션을 구축하는 데 필수적인 지식입니다.

댓글 남기기

Wordpress Social Share Plugin powered by Ultimatelysocial
Copy link
URL has been copied successfully!
THREADS
RSS
error: 저작권 콘텐츠보호를 부탁드립니다.