App Router vs Pages Router
App Router와 Pages Router의 파일 구조·데이터 페칭·컴포넌트 경계·전환 비용을 비교해 선택 기준을 세웁니다.
Next.js를 처음 배우면 기능보다 구조를 고르는 데서 먼저 막히는 경우가 많습니다. Pages Router로 시작해도 되는지, App Router를 바로 쓰는 게 맞는지 같은 질문은 입문자뿐 아니라 기존 프로젝트를 운영하는 팀에서도 자주 나옵니다. 겉으로는 폴더 구조의 차이처럼 보이지만, 실제로는 데이터 페칭 방식과 서버·클라이언트 컴포넌트의 구분, 성능 전략까지 바뀌는 선택입니다.
이 글에서는 두 라우터를 비교표로 끝내지 않고, 어떤 프로젝트에서 어떤 선택이 더 유리한지 판단하는 기준까지 정리합니다. 읽고 나면 새 프로젝트를 시작할 때와 기존 프로젝트를 유지보수할 때 각각 어떤 결정을 내리는 것이 합리적인지 감을 잡을 수 있습니다.
Pages Router: 익숙하고 효율적인 방식
Next.js의 초기부터 함께해 온 Pages Router는 파일 시스템 기반 라우팅을 씁니다. pages 디렉터리 안에 React 컴포넌트를 정의하면 그 파일의 경로가 웹 애플리케이션의 URL 경로로 자동으로 연결되므로, 예를 들어 pages/about.js 파일은 /about 경로에서 렌더링됩니다.
Pages Router는 직관적이고 설정이 간단해 페이지를 빠르게 만들 수 있고, React 개발자에게 익숙한 방식이라 새 개념을 많이 익히지 않아도 됩니다. 데이터는 getServerSideProps, getStaticProps, getStaticPaths 같은 함수로 준비해 서버 측 렌더링(SSR)이나 정적 사이트 생성(SSG)을 구현합니다.
기존의 Pages Router 코드와 운영 경험을 그대로 활용할 수 있다면, 유지보수나 빠른 프로토타이핑에서 전환 비용을 줄일 수 있습니다. 다만 프로젝트가 작다는 이유만으로 Pages Router가 더 알맞은 것은 아니며, 중첩 레이아웃과 서버 컴포넌트, 스트리밍을 중심으로 설계하려는 프로젝트라면 App Router가 더 자연스러운 선택입니다.
App Router: 지금의 기본 구조
Next.js 13부터 도입된 App Router는 React Server Components(RSC)와 스트리밍(Streaming)을 바탕으로 설계되었으며, app 디렉터리 안에서 라우팅과 컴포넌트 구조를 정의해 Pages Router와는 근본적으로 다른 방식을 씁니다.
React Server Components는 서버에서 처리할 UI와 브라우저에 보내야 할 UI를 나누어 클라이언트 번들의 크기를 줄일 수 있게 합니다. 스트리밍은 페이지의 일부를 먼저 보내고 나머지를 이어서 보내 첫 응답의 경험을 개선하며, 레이아웃(Layouts)은 여러 페이지가 함께 쓰는 UI를 쉽게 정의하고 중첩할 수 있어 재사용과 유지보수에 유리합니다. 데이터는 fetch API를 확장한 방식으로 요청과 캐싱 전략을 세밀하게 정할 수 있고, HTML 출력과 메타데이터, 캐싱, 동적 렌더링의 범위를 분명히 관리하면 검색 노출과 성능을 함께 개선할 수 있습니다.
App Router는 처음 배울 때 Pages Router보다 어려울 수 있지만, 복잡한 애플리케이션에서 레이아웃과 서버·클라이언트의 구분, 데이터 페칭 정책을 함께 설계할 때 장점이 큽니다. Pages Router도 계속 지원되지만, 새 프로젝트라면 App Router를 기본 선택지로 두는 편이 공식 문서의 방향과도 맞습니다.
두 라우터에서 각 일을 맡는 위치를 비교하면 다음과 같습니다. URL과 레이아웃은 Pages에서 pages/about.js가 /about을 만들고, App에서는 app/about/page.js가 페이지를 노출하며 layout.js로 중첩된 공통 UI를 정의합니다. 서버 데이터는 Pages에서 페이지의 getServerSideProps나 getStaticProps로 준비하고, App에서는 서버 컴포넌트 안에서 데이터 요청과 캐시 정책을 정할 수 있습니다.
실행 위치를 보면, App의 페이지와 레이아웃은 기본적으로 서버 컴포넌트이고 상태나 이벤트, 브라우저 API가 필요한 곳에만 클라이언트 컴포넌트를 둡니다. Pages도 서버에서 HTML을 렌더링할 수 있으므로 모두 브라우저 전용이라는 뜻은 아닙니다. 첫 응답과 메타데이터는 App에서 Suspense와 로딩 UI, Metadata API를 라우트 구조에 연결하며, 어느 라우터를 쓰든 실제 성능과 검색 노출은 출력과 데이터, 배포 조건을 함께 확인해야 합니다.
클라이언트 컴포넌트도 처음 들어올 때는 서버에서 HTML을 미리 만들 수 있습니다. use client는 브라우저에서만 렌더링한다는 선언이 아니라, 클라이언트 모듈이 시작되는 지점을 정하는 표시입니다. 자세한 실행 과정은 Next.js 문서의 서버·클라이언트 컴포넌트를 기준으로 뒤의 장에서 다룹니다.
NEXT · CH1-2
같은 URL도 책임을 두는 파일이 다릅니다
두 열은 서로 다른 라우터의 선택지입니다. Pages도 서버 HTML을 만들 수 있고, App의 Client Component도 첫 진입에 서버 HTML을 준비할 수 있습니다.
Pages Router
- URL /about
pages/about.js- 공통 UI
pages/_app.js와 페이지별 구성으로 관리합니다.- 서버 데이터
- 페이지의
getServerSideProps·getStaticProps등에서 준비합니다. - 실행 경계
- 서버 HTML 렌더링과 브라우저 상호작용을 지원합니다. App의 Server Component 모델과 구분합니다.
App Router
- URL /about
app/about/page.js- 공통 UI
layout.js를 중첩하고children에 페이지를 넣습니다.- 서버 데이터
- Server Component에서 데이터 요청과 캐시 정책을 정합니다.
- 실행 경계
- 페이지·레이아웃은 기본 Server Component입니다. 상태·이벤트에 Client 경계를 둡니다.
점진 전환할 때
두 디렉터리는 공존할 수 있지만 같은 URL을 양쪽에서 정의할 수는 없습니다. 전환할 라우트의 URL·사용자 흐름·초기 화면·배포 절차를 확인합니다.
새 프로젝트의 App Router 선택과 기존 Pages 서비스의 전환 비용은 별도로 판단합니다. 어느 쪽도 성능·검색 순위를 자동 보장하지 않습니다.
위 그림은 같은 URL이라도 두 라우터에서 일을 맡는 파일이 다르다는 점을 나란히 보여 줍니다. 그림의 요약처럼 두 열은 서로 다른 라우터의 선택지이며, Pages도 서버 HTML을 만들 수 있고 App의 클라이언트 컴포넌트도 첫 진입에 서버 HTML을 준비할 수 있습니다. 왼쪽의 Pages Router에서 URL /about은 pages/about.js가 만들고, 공통 UI는 pages/_app.js와 페이지별 구성으로 관리하며, 서버 데이터는 페이지의 getServerSideProps나 getStaticProps에서 준비하고, 서버 HTML 렌더링과 브라우저 상호작용을 지원합니다.
오른쪽의 App Router에서는 app/about/page.js가 같은 URL을 만들고, layout.js를 중첩해 children에 페이지를 넣으며, 서버 컴포넌트에서 데이터 요청과 캐시 정책을 정하고, 페이지와 레이아웃은 기본 서버 컴포넌트라 상태와 이벤트가 필요한 곳에만 클라이언트 컴포넌트를 둡니다. 그림 아래의 칸은 점진적으로 옮길 때 두 디렉터리가 함께 있을 수는 있지만 같은 URL을 양쪽에서 정의할 수는 없으므로, 옮길 라우트의 URL과 사용자 흐름, 첫 화면, 배포 절차를 확인하라고 짚습니다. 그림의 마지막 설명처럼 새 프로젝트에서 App Router를 고르는 일과 기존 Pages 서비스의 전환 비용은 따로 판단하며, 어느 쪽도 성능과 검색 순위를 저절로 보장하지 않습니다.
어떤 라우터를 선택해야 할까요?
이 교재는 App Router를 중심으로 Next.js 16의 주요 기능을 다룹니다. App Router는 서버 컴포넌트와 스트리밍, 중첩 레이아웃, 캐싱 전략을 한 흐름에서 설계할 수 있게 해 주며, 기존 Pages Router의 마이그레이션도 다루지만 새 프로젝트의 기준은 App Router로 설명합니다.
다만 모든 프로젝트가 곧바로 App Router로 옮겨야 하는 것은 아닙니다. 이미 안정적으로 운영 중인 서비스라면 라우트 단위로 조금씩 옮기면서 테스트와 배포의 위험을 관리하는 편이 현실적이며, 무조건 최신을 고르기보다 지금 서비스의 안정성과 전환 비용을 함께 따지는 것이 좋은 선택입니다.
라우터를 고르기 전에는 새 프로젝트인지 기존 서비스를 조금씩 옮기는 것인지, 서버 컴포넌트와 스트리밍을 당장 쓸 계획이 있는지, 팀이 getServerSideProps나 getStaticProps 중심의 코드를 얼마나 가지고 있는지, 배포 일정상 마이그레이션의 위험을 감당할 수 있는지를 먼저 정리합니다. 새 프로젝트는 App Router의 기본 구조로 시작하고 기존 서비스는 라우트 단위로 옮길 범위를 먼저 정하며, 팀 규칙 문서에 “서버 컴포넌트를 우선하고 클라이언트 컴포넌트는 최소로” 같은 원칙을 적어 두고, 옮기기 전과 후의 빌드 시간과 번들 크기, 첫 로딩 지표를 같은 조건에서 비교합니다.
두 라우터를 함께 둘 때
Pages Router와 App Router는 점진적인 전환을 위해 함께 둘 수 있지만, 같은 URL을 양쪽에서 정의할 수는 없습니다. 옮기는 라우트마다 기존 URL과 사용자 흐름의 회귀, 첫 JavaScript와 첫 화면의 성능, 배포 절차와 팀 규칙이 바뀌는 비용을 확인하며, 함께 둘 때의 제약은 Next.js 문서의 App Router 마이그레이션 안내를 따릅니다.
예를 들어 기존 pages/about.js가 /about을 제공하는 서비스에 새 app/about/page.js를 더했다고 해 봅시다. 폴더 이름은 달라도 두 파일이 만들려는 URL은 같습니다. 함께 둘 수 있다는 것이 같은 URL의 두 구현을 동시에 켜 둔다는 뜻은 아니므로, /about을 옮길 때는 App 쪽 구현을 준비한 뒤 그 URL을 맡던 Pages 파일을 지우거나 라우트 밖으로 옮기고, 기존 요청과 화면을 다시 확인합니다. 공통 컴포넌트는 각 라우터의 규칙을 검토해 재사용할 수 있습니다.
더 깊이: 두 라우터가 쓰는 React 버전과 그 사이의 이동
한 프로젝트 안에 두 라우터가 있어도 실제로 실행되는 React는 같지 않을 수 있습니다. Next.js 문서에 따르면 App Router는 Next.js에 내장된 React 카나리(canary) 릴리스를 씁니다. 이 릴리스에는 안정판 React 19의 변경이 모두 들어 있고, 정식 출시 전에 프레임워크에서 검증하는 새 기능도 함께 들어 있습니다. 반면 Pages Router는 프로젝트의 package.json에 설치한 React 버전을 그대로 씁니다. 그래서 package.json의 react 버전만 보고 App Router에서 어떤 React 기능이 동작하는지 판단할 수는 없으며, 같은 컴포넌트가 두 라우터에서 다르게 동작한다면 이 차이부터 확인합니다.
두 라우터 사이를 오가는 이동 방식도 다릅니다. 서로 다른 라우터가 맡은 경로 사이를 이동하면 클라이언트 측 이동이 아니라 문서 전체를 다시 불러오는 하드 내비게이션(hard navigation)이 일어나고, next/link의 자동 프리페치(prefetch)도 다른 라우터의 경로는 미리 가져오지 않습니다. 따라서 점진적으로 옮기는 동안 자주 오가는 화면이 서로 다른 라우터에 나뉘어 있으면, 그 사이를 이동할 때마다 전체 문서를 새로 받고 클라이언트 상태도 새로 시작합니다. 그래서 옮길 범위를 정할 때는 한 사용자 흐름에서 자주 이어지는 화면들을 같은 단계에서 함께 옮기는 편이 좋습니다.
정리
- Pages Router는
pages디렉터리의 파일로 URL을 만들고,getServerSideProps나getStaticProps로 데이터를 준비합니다. - App Router는
app디렉터리의page.js와layout.js로 라우트와 중첩 레이아웃을 만들고, 기본이 서버 컴포넌트입니다. use client는 브라우저 전용 렌더링이 아니라 클라이언트 모듈이 시작되는 지점이며, 클라이언트 컴포넌트도 첫 진입에 서버 HTML을 만들 수 있습니다.- 새 프로젝트는 App Router를 기본으로 고르고, 기존 서비스는 라우트 단위로 전환 비용을 따져 조금씩 옮깁니다.
- 두 라우터는 함께 둘 수 있지만 같은 URL을 양쪽에서 정의할 수는 없습니다.
연습 문제
1. 옮길 페이지 고르기
프로젝트에 pages/about.js와 pages/contact.js가 있습니다. 다른 라우트는 그대로 두고 /about만 App Router로 옮기려면 어느 기존 페이지의 URL을 넘겨야 할까요? 새 기능을 app/products/page.js에 더하는 경우도 같은 충돌일까요?
해설 보기
app/about/page.js를 켜려면 기존에 /about을 정의한 pages/about.js와의 중복을 없애야 합니다. pages/contact.js는 /contact이므로 이번 전환 때문에 지울 필요가 없습니다.
app/products/page.js는 /products를 정의하므로, 그 URL의 다른 구현이 없다면 앞의 두 Pages 경로와 충돌하지 않습니다. /about을 옮긴 뒤에는 데이터 준비와 레이아웃, 라우팅 훅을 App의 규칙으로 다시 확인하며, 파일을 옮기는 것만으로 모든 Pages API가 호환된다고 판단하지 않습니다.
2. Client Component의 첫 HTML
App Router에서 'use client'를 붙인 컴포넌트는 서버에서 전혀 렌더링되지 않고 브라우저에서만 그려진다고 생각한 동료가 있습니다. 이 생각은 맞을까요?
해설 보기
맞지 않습니다. 'use client'는 그 파일부터 클라이언트 모듈이 시작된다는 표시이며, 클라이언트 컴포넌트도 처음 들어올 때는 서버에서 HTML을 미리 만들 수 있습니다. 브라우저는 그 HTML을 먼저 보여 준 뒤 JavaScript로 상호작용을 연결합니다.
차이는 클라이언트 컴포넌트의 코드가 브라우저로도 보내져 상태와 이벤트를 다룰 수 있다는 점이므로, 상호작용이 필요 없는 부분까지 클라이언트 컴포넌트로 만들면 브라우저로 보내는 JavaScript만 늘어납니다.
3. 운영 중인 서비스의 전환
수백 개의 페이지가 Pages Router로 안정적으로 운영되고 있고, 다음 주에 큰 배포 일정이 있습니다. 팀장이 이번 주에 모든 페이지를 App Router로 옮기자고 합니다. 어떤 기준으로 판단해야 할까요?
해설 보기
한꺼번에 옮기기보다 라우트 단위로 조금씩 옮기는 편이 현실적입니다. 두 라우터는 함께 둘 수 있으므로, 서버 컴포넌트나 스트리밍의 이득이 큰 라우트부터 범위를 정해 옮기고, 그 라우트의 URL과 사용자 흐름, 첫 화면 성능, 배포 절차를 확인합니다.
큰 배포 일정이 가까운 상황에서는 마이그레이션의 위험을 감당할 수 있는지가 핵심이며, 최신 구조라는 이유만으로 안정성과 전환 비용을 무시하지 않습니다.
다음 글 학습 목표 및 전제 조건에서는 이 교재의 학습 목표와 필요한 사전 지식을 정리합니다.