저번 포스팅까지 Page Router를 기반으로한 Next.js에 대해서 학습하였다.
그렇다면 Page Router의 장단점을 정리해보고 최신 라우팅 방식인 App Router가 나오게된 배경을 살펴보자.
Page Router의 장점
- 파일 시스템 기반의 간편한 라우팅 방식
- pages 디렉토리 안에 파일을 생성하는 것만으로 자동으로 라우팅 설정
- /pages/about.tsx -> /about
- 다양한 사전 렌더링 방식 제공 (SSR, SSG, ISR)
Page Router의 단점
- 페이지별 레이아웃 설정이 번거로움
- 페이지마다 레이아웃을 중복 구현하게 되는 문제 발생
- 데이터 패칭이 페이지 컴포넌트에 집중됨
- 서버 측 데이터 패칭을 getServerSideProps 또는 getStaticProps와 같은 함수 안에서 작성해야함
- 해당 함수들은 페이지 컴포넌트에서만 동작하기 때문에 하위 컴포넌트에서는 직접 데이터를 불러올 수 없음
- 재사용성과 관심사 분리에 제약이 생김
- 불필요한 컴포넌트까지 JS 번들에 포함
- 상호작용이 없는 컴포넌트들은 hydration 과정에서는 다시 실행될 필요 없음
- 코드 분할에 한계가 있어 이러한 컴포넌트들도 모두 번들에 포함되어 초기 로딩 속도에 악영향
위와 같은 한계들을 해결하기 위해 Next.js 13부터 App Router가 도입되었다.
App Router
App Router의 라우팅 방식
- app 디렉토리 아래의 page.tsx 파일만 라우팅됨
- /search: app/search/page.tsx
- /book/123: app/book/[id]/page.tsx
- 파라미터 또는 쿼리스트링은 페이지 컴포넌트에 자동으로 props로 전달됨
// 파라미터: params로 전달
export default async function Book({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
return <div>book/{id} page</div>;
}
// 쿼리스트링: searchParams로 전달
export default async function Search({searchParams}: {searchParams: Promise<{q: string}>}) {
const { q } = await searchParams;
return <div>Search 페이지: {q}</div>;
}
App Router의 공통 레이아웃 설정
- 각 디렉토리 별 layout.tsx 파일을 통해 공통 레이아웃 설정
- ex) /app/search/layout.tsx
- /search로 시작하는 모든 페이지에 적용
- 반드시 children을 props로 포함해야함
- 하위 디렉토리에 다른 layout.tsx가 있는 경우 중첩되어 사용됨 ex) /search/setting/layout.tsx
- 라우트 그룹
- 소괄호로 감싸진 디렉토리로 서로 다른 경로의 페이지를 하나로 묶기 위해 사용
- 일정 페이지에만 동일한 레이아웃 적용하기 위해 사용
- 실제 경로에는 영향을 끼치지 않음
서버 컴포넌트
- 리액트18부터 새롭게 추가된 유형의 컴포넌트로 서버측에서만 실행되는 컴포넌트 (브라우저에서 실행 x)
- js번들에 포함되지 않아 hydration 과정에서 실행되지 않음
- 기본적으로 app router의 모든 컴포넌트들은 서버 컴포넌트이며, 필요한 경우에만 클라이언트 컴포넌트 설정을 해주게 됨
- react hooks 등 브라우저에서만 동작하는 코드를 사용할 수 없음
- "use client";를 파일 맨 위에 명시하여 클라이언트 컴포넌트로 사용할 수 있음
※ 서버 컴포넌트 주의사항
- 서버 컴포넌트에는 브라우저에서 실행될 코드가 포함되면 안됨
- 브라우저에서 실행되는 기능을 담고있는 라이브러리를 호출하는 것도 에러의 원인이 될 수 있음
- 클라이언트 컴포넌트는 클라이언트에서만 실행되지 않음
- 서버와 클라이언트에서 각각 한 번 씩 실행
- 클라이언트 컴포넌트에서 서버 컴포넌트를 import 할 수 없음
- 서버 컴포넌트를 import 하면 자동으로 클라이언트 컴포넌트로 전환됨
- 이 경우, import 방식이 아닌 서버 컴포넌트를 props의 children으로 받아 렌더링하는 방식으로 해결
- 서버 컴포넌트에서 클라이언트 컴포넌트에게 직렬화 되지 않는 props는 전달할 수 없음
- 직렬화: 객체, 배열 등 데이터를 문자열이나 byte형식으로 바꿔 네트워크를 통해 전달할 수 있도록 하는 것
- 함수는 직렬화가 불가능함 (RSC Payload 생성 과정에서 런타임 에러)
※ RSC Payload
- 서버 컴포넌트의 실행 결과 생기는 데이터
- 서버 컴포넌트의 렌더링 결과
- 연결된 클라이언트 컴포넌트의 위치
- 클라이언트 컴포넌트에게 전달할 props

app router에서 서버 컴포넌트가 추가됨에 따라 사전 렌더링 과정에 약간의 변경점이 생겼다.
기존에 전달되는 js번들에는 이제 클라이언트 컴포넌트의 데이터만 포함되기 때문에 서버 컴포넌트의 실행 결과물인 RSC Payload도 클라이언트에게 함께 전달되게 된다. 클라이언트는 이 RSC Payload와 js번들을 적절히 합쳐 페이지 교체를 실행하게 된다.
App Router의 네비게이팅
- Link 컴포넌트 사용
- useRouter 사용 (next/navigation)
- next/router는 페이지 라우터용 useRouter임을 주의
App Router의 데이터 패칭
기존 페이지 라우터에서는 모든 컴포넌트가 클라이언트 컴포넌트로 동작하기 때문에 페이지 컴포넌트 내에서 데이터 패칭을 하게 되면 매우 비효율적이다. 따라서 getServerSideProps나 getStaticProps와 같은 메소드를 통해 SSR 또는 SSG 방식으로 데이터 패칭이 이루어지도록 하였다. 하지만 앱 라우터에서는 서버 컴포넌트의 등장으로 더 편리한 방식으로 데이터 패칭을 수행할 수 있게 되었다.

서버 컴포넌트 자체를 async 키워드를 통해 비동기 함수로 만든 후, 그 안에서 직접 데이터 패칭을 수행할 수 있다.
이를 통해 더 간결한 코드 작성은 물론, props 등으로 넘겨줄 필요 없이 하위 컴포넌트에서도 직접 데이터 패칭을 수행할 수 있어 기존 페이지 라우터의 단점을 보완하였다.
데이터 캐싱
fetch 메소드를 통해 불러온 데이터를 Next 서버에서 보관하는 기능으로 불필요한 데이터의 요청 수를 줄여서 성능을 개선할 수 있다. fetch 메소드의 두 번째 인자를 통해 설정할 수 있으며, Next 자체적으로 fetch 메소드를 확장한 기능이기 때문에 axios 등 라이브러리에서는 활용 불가능하다.
const response await fetch(`~/api`, {cache:'force-cache'});
- { cache : 'force-cache' }
- 요청의 결과를 무조건 캐싱
- 한 번 호출된 이후에는 다시 호출되지 않음
- { cache : 'no-store' }
- 데이터 페칭의 결과를 저장하지 않는 옵션
- 캐싱을 아예 하지 않도록 설정
- Next15부터 default 값
- { next : { revalidate : 3 } }
- 특정 시간을 주기로 캐시를 업데이트(3초)
- 페이지 라우터의 ISR 방식과 유사함
- { next : { tags : ['a'] } }
- 요청이 들어왔을 때 데이터를 최신화
- On-Demand Revalidate
Request Memoization
Request Memoization이란 하나의 페이지를 이루고 있는 여러 개의 컴포넌트에서 발생하는 api 요청 중에서 중복으로 발생하는 요청을 캐싱하여 최적화하는 것을 말한다.
데이터 캐싱과 다르게 렌더링이 종료되면 모든 캐시가 소멸되므로 단순히 중복 api의 호출을 방지하는 것에만 목적이 있다.
※ 본 포스팅은 인프런 이정환 Winterlood님의 한 입 크기로 잘라먹는 Next.js(15+) 강의를 기반하여 정리하였습니다.
'Web-Frontend > Next.js' 카테고리의 다른 글
| [Next.js] 앱 라우터 - 스트리밍과 에러 핸들링 (0) | 2025.06.20 |
|---|---|
| [Next.js] App Router와 페이지 캐싱(풀 라우트 캐시) (0) | 2025.06.13 |
| [Next.js] 사전 렌더링과 데이터 패칭 (Page Router) (0) | 2025.05.20 |
| [Next.js] getLayout 패턴 (Page Router) (0) | 2025.05.15 |
| [Next.js] 프리패칭(pre-fetching) (0) | 2025.05.10 |