모든 블로그 포스트
기술 트렌드2023-08-05 (토요일)

Next.js Pages에서 App Router로 마이그레이션하기

김개발10분 읽기
Next.js Pages에서 App Router로 마이그레이션하기

Next.js Pages에서 App Router로 마이그레이션하기

Next.js 13에서 도입된 App Router는 더 강력한 라우팅 기능과 React 서버 컴포넌트를 활용할 수 있는 새로운 방식을 제공합니다. 이 글에서는 기존 Pages Router에서 App Router로 프로젝트를 마이그레이션하는 전략과 모범 사례를 살펴보겠습니다.

App Router의 주요 이점

  1. 서버 컴포넌트: 기본적으로 모든 컴포넌트는 서버 컴포넌트로 작동하며, 클라이언트로 전송되는 JavaScript를 줄일 수 있습니다.
  2. 중첩 라우팅: 레이아웃, 로딩 상태, 오류 처리 등을 중첩된 방식으로 처리할 수 있습니다.
  3. 병렬 라우트: 독립적으로 렌더링되는 여러 페이지를 동시에 표시할 수 있습니다.
  4. 데이터 가져오기 간소화: 컴포넌트 내에서 직접 비동기 데이터를 가져올 수 있습니다.

점진적 마이그레이션 전략

1. 프로젝트 설정 준비

기존 Pages Router 구조를 유지하면서 App Router를 병행할 수 있습니다:

/pages        # 기존 Pages Router
/app          # 새로운 App Router

2. 공통 컴포넌트 준비하기

서버/클라이언트 컴포넌트 구분을 확실히 하세요:

// components/Button.tsx
'use client'; // 클라이언트 컴포넌트 표시

export default function Button({ children }) {
  return (
    <button onClick={() => console.log('clicked')}>
      {children}
    </button>
  );
}

3. 라우트 마이그레이션 시작하기

간단한 페이지부터 마이그레이션을 시작하세요:

// pages/about.js (기존)
export default function About() {
  return <h1>About Us</h1>;
}

// app/about/page.tsx (새로운 방식)
export default function About() {
  return <h1>About Us</h1>;
}

주요 변경 사항 적용하기

1. 데이터 가져오기 방식 변경

// pages/posts/[id].js (기존)
export async function getServerSideProps({ params }) {
  const res = await fetch(`https://api.example.com/posts/${params.id}`);
  const post = await res.json();
  return { props: { post } };
}

export default function Post({ post }) {
  return <h1>{post.title}</h1>;
}

// app/posts/[id]/page.tsx (새로운 방식)
async function getPost(id) {
  const res = await fetch(`https://api.example.com/posts/${id}`);
  return res.json();
}

export default async function Post({ params }) {
  const post = await getPost(params.id);
  return <h1>{post.title}</h1>;
}

2. 레이아웃 구현하기

// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ko">
      <body>
        <header>
          <nav>{/* 네비게이션 */}</nav>
        </header>
        <main>{children}</main>
        <footer>{/* 푸터 내용 */}</footer>
      </body>
    </html>
  );
}

3. 메타데이터 처리

// app/posts/[id]/page.tsx
import type { Metadata } from 'next';

export async function generateMetadata(
  { params }: { params: { id: string } }
): Promise<Metadata> {
  const post = await getPost(params.id);
  
  return {
    title: post.title,
    description: post.excerpt,
  };
}