Page Router vs App Router

Stoic Park·

개요

최근 Next.js를 접하게 되면서 자연스레 App Router로 라우팅을 접하게 됐습니다. 다만 여러 레퍼런스를 참고할 때 Page Router에 대한 내용도 자주 등장하곤 합니다. 그래서 오늘은 App RouterPage Router의 차이점을 정리해보려고 합니다.

App Router란?

Next.js 13에서 새롭게 도입된 App Router는 기존 Page Router보다 유연하고 현대적인 방식으로 라우팅을 관리할 수 있도록 설계되었습니다.

특징

  • 폴더 기반 라우팅: app/ 디렉토리를 기반으로 라우팅이 자동으로 설정됩니다.
  • 서버 컴포넌트 지원: 클라이언트 컴포넌트와 서버 컴포넌트를 함께 사용할 수 있습니다.
  • React Server Components(RSC): 데이터를 서버에서 직접 처리하여 클라이언트로 전달할 수 있습니다.
  • 레이아웃 기능 지원: 전역적인 레이아웃을 손쉽게 관리할 수 있습니다.
  • 병렬 라우팅 및 인터셉트 가능: 더 강력한 라우팅 기능을 제공합니다.

사용 예시

// app/page.tsx
export default function Home() {
 return <h1>Welcome to App Router</h1>
}

Page Router란?

기존의 Page Router는 Next.js 초기부터 제공되던 방식으로, 파일 기반의 라우팅 시스템입니다.

특징

  • 파일 기반 라우팅: pages/ 디렉토리에 파일을 생성하면 해당 경로로 자동 라우팅됩니다.
  • getStaticProps, getServerSideProps 지원: 데이터를 미리 가져오거나, 요청마다 새로운 데이터를 불러올 수 있습니다.
  • Client-side Rendering (CSR) 위주: 클라이언트에서 주로 처리하며, 필요에 따라 서버 렌더링도 지원합니다.

사용 예시

// pages/index.tsx
export default function Home() {
 return <h1>Welcome to Page Router</h1>
}

차이점

  1. 디렉토리 구조

    • Page Router: /pages 디렉토리 사용
    • App Router: /app 디렉토리 사용
  2. 렌더링 방식

    • Page Router: 기본적으로 클라이언트 컴포넌트
    • App Router: 기본적으로 서버 컴포넌트
  3. 데이터 페칭

    // Page Router - pages/posts/[id].tsx
    export async function getStaticProps({ params }) {
     const post = await fetchPost(params.id)
     return { props: { post } }
    }
    
    // App Router - app/posts/[id]/page.tsx
    async function PostPage({ params }: { params: { id: string } }) {
     const post = await fetchPost(params.id)
     return <div>{post.title}</div>
    }
  4. 라우팅 훅 사용

    // Page Router
    import { useRouter } from 'next/router'
    
    function Component() {
     const router = useRouter()
    
     return (
      <div>
       Current path: {router.pathname}
       Query: {router.query.id}
       <button onClick={()=> router.push('/about')}>Go to About</button>
      </div>
     )
    }
    
    // App Router
    import { useRouter, usePathname, useSearchParams } from 'next/navigation'
    
    function Component() {
     const router = useRouter()
     const pathname = usePathname()
     const searchParams = useSearchParams()
    
     return (
      <div>
       Current path: {pathname}
       Query: {searchParams.get('id')}
       <button onClick={()=> router.push('/about')}>Go to About</button>
      </div>
     )
    }
  5. 레이아웃 구현

    // Page Router - pages/_app.tsx
    function MyApp({ Component, pageProps }) {
     return (
      <Layout>
       <Component {...pageProps} />
      </Layout>
     )
    }
    
    // App Router - app/layout.tsx
    export default function RootLayout({
     children,
    }: {
     children: React.ReactNode
    }) {
     return (
      <html lang="en">
       <body>
        <nav>Navigation</nav>
        {children}
       </body>
      </html>
     )
    }
  6. 에러 처리

    // Page Router - pages/404.js
    export default function Custom404() {
     return <h1>404 - Page Not Found</h1>
    }
    
    // App Router - app/not-found.tsx
    export default function NotFound() {
     return <h1>404 - Page Not Found</h1>
    }
  7. 동적 라우트

    // Page Router - pages/posts/[...slug].tsx
    function Post() {
     const router = useRouter()
     const { slug } = router.query // ['a', 'b', 'c']
     return <p>Post: {slug.join('/')}</p>
    }
    
    // App Router - app/posts/[...slug]/page.tsx
    function Post({ params }: { params: { slug: string[] } }) {
     return <p>Post: {params.slug.join('/')}</p>
    }
  8. 로딩 상태

    // Page Router
    function Post() {
     const router = useRouter()
    
     if (router.isFallback) {
      return <div>Loading...</div>
     }
     return <div>Content</div>
    }
    
    // App Router - app/posts/loading.tsx
    export default function Loading() {
     return <div>Loading...</div>
    }

주요 API 차이점 정리

기능Page RouterApp Router
라우팅 훅next/routernext/navigation
데이터 페칭getStaticProps/getServerSidePropsasync 컴포넌트 + fetch
페이지 정의pages/about.tsxapp/about/page.tsx
레이아웃_app.tsx, _document.tsxlayout.tsx
에러 페이지_error.tsx, 404.tsxerror.tsx, not-found.tsx
로딩 상태수동 구현 필요loading.tsx
메타데이터Head 컴포넌트metadata 객체/generateMetadata

결론

Next.js 13 이후로 App Router가 등장하면서 더욱 유연하고 강력한 라우팅을 제공하게 되었습니다. 특히 서버 컴포넌트를 활용한 최적화가 가능해졌으며, 레이아웃 관리도 더 쉬워졌습니다. 다만, 기존 프로젝트에서는 여전히 Page Router가 널리 사용되고 있으므로, 프로젝트의 특성에 맞게 선택하는 것이 중요합니다.

만약 새로운 프로젝트를 시작한다면, App Router를 사용하는 것이 권장됩니다.

Reference