Next.js App Router 마이그레이션 가이드



Next.js 12/13 Pages Router에서 App Router로 마이그레이션하는 가이드입니다.



App Router 디렉토리 구조


app/
layout.tsx # 루트 레이아웃 (필수)
page.tsx # 루트 페이지 (/)
loading.tsx # 로딩 UI
error.tsx # 에러 핸들링
not-found.tsx # 404 페이지

dashboard/
layout.tsx # 중첩 레이아웃
page.tsx # /dashboard 페이지

blog/
[slug]/
page.tsx # 동적 라우팅


Pages → App Router 변환


// Pages Router (pages/index.tsx)
export default function Home() {
return <div>Home</div>;
}

// App Router (app/page.tsx)
export default function Home() {
return <div>Home</div>;
}


Layout 설정


// app/layout.tsx (필수)
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ko">
<body>
<header>헤더</header>
<main>{children}</main>
<footer>푸터</footer>
</body>
</html>
);
}


데이터 페칭 변경


// Pages Router - getServerSideProps
export async function getServerSideProps() {
const data = await fetch("...");
return { props: { data } };
}

// App Router - 서버 컴포넌트에서 직접 fetch
async function Page() {
const data = await fetch("...", { cache: "no-store" });
return <div>{data}</div>;
}

// App Router - 정적 생성 (getStaticProps 대체)
async function Page() {
const data = await fetch("...", { cache: "force-cache" });
return <div>{data}</div>;
}


클라이언트 컴포넌트


// 상단에 "use client" 지시어 필요
"use client";

import { useState } from "react";

export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}


메타데이터 설정


// Pages Router - Head 컴포넌트
import Head from "next/head";
<Head><title>페이지 제목</title></Head>

// App Router - metadata 객체
export const metadata = {
title: "페이지 제목",
description: "페이지 설명",
};

// 동적 메타데이터
export async function generateMetadata({ params }) {
return { title: params.slug };
}


라우팅 차이점








기능Pages RouterApp Router
동적 라우트[id].tsx[id]/page.tsx
레이아웃_app.tsxlayout.tsx
에러_error.tsxerror.tsx
404404.tsxnot-found.tsx


마이그레이션 팁



  • 점진적 마이그레이션 가능 (pages/와 app/ 공존)

  • 서버 컴포넌트가 기본값

  • 상태/이벤트 사용 시 "use client" 필요