같은 Next.js지만 사고방식이 달라
Next.js는 기존 프로젝트를 위해 Pages Router도 계속 지원해. 하지만 새 작업의 기준은 App Router야. 단순히 pages/를 app/으로 바꾸는 일이 아니라, 데이터를 읽고 화면을 조립하는 방식 자체가 달라졌어.
| 항목 | Pages Router | App Router |
|---|---|---|
| 기본 디렉터리 | pages/ | app/ |
| 기본 컴포넌트 | 클라이언트 중심 | 서버 컴포넌트 |
| 데이터 읽기 | getServerSideProps, getStaticProps | 비동기 컴포넌트에서 직접 읽기 |
| 레이아웃 | _app.tsx에서 수동 조립 | 세그먼트별 layout.tsx |
| 로딩 화면 | 직접 연결 | loading.tsx와 Suspense |
| 오류 화면 | _error.tsx | error.tsx와 global-error.tsx |
| 스트리밍 | 제한적 | 핵심 기능으로 지원 |
| 서버 액션 | 없음 | 지원 |
페이지보다 트리를 생각해
Pages Router에서는 데이터를 준비하는 함수와 페이지를 한 쌍으로 보는 습관이 강했어. App Router에서는 레이아웃, 서버 컴포넌트, 클라이언트 섬이 하나의 트리를 이뤄. “한 페이지가 무엇을 가져오나?”보다 “어느 컴포넌트가 어떤 책임을 가지나?”가 더 중요한 질문이 돼.
이사는 라우트 단위로
두 라우터는 한 프로젝트 안에서 공존할 수 있어. 한 번에 갈아엎지 말고 실제로 손대는 라우트부터 옮겨. 다만 같은 주소를 양쪽에 동시에 만들면 App Router가 우선하니, 옮긴 뒤 옛 파일은 바로 정리해야 해.
기존 Pages Router 앱을 옮길 때는 URL 하나를 골라 데이터 읽기, 레이아웃, 로딩, 오류의 주인을 함께 다시 정해. 파일만 app/으로 복사하면 getServerSideProps 중심 사고가 props 배관으로 남아. 옮긴 라우트가 서버 컴포넌트에서 직접 읽고 필요한 잎만 클라이언트가 되는지 확인해야 진짜 이사야. 두 라우터가 공존할 수 있다는 건 두 사고방식을 영원히 섞어도 된다는 뜻이 아니야. 전환 기간에는 안전하지만, 공통 기능이 Pages와 App용으로 두 벌 생기기 시작하면 유지비가 빠르게 커져. 새 기능의 기본 위치와 옛 라우트 종료 순서를 팀 계약으로 정해.
옮길 라우트 하나에서 이전 getServerSideProps의 각 줄을 데이터 읽기·권한·화면 props로 분류해. App Router 버전에서는 각 책임이 어느 서버 컴포넌트와 경계로 이동했는지 diff에 표시하고, 옛 주소가 남아 우선순위 충돌을 만들지 않는지 직접 방문해 봐.