Next.js App Router 완벽 가이드
Next.js 13 이후 도입된 App Router의 핵심 개념과 실전 사용법을 단계별로 알아봅니다. 서버 컴포넌트, 레이아웃, 라우팅 등을 상세히 다룹니다.
Next.js App Router 완벽 가이드
Next.js는 React 기반의 풀스택 프레임워크로, 2023년 이후 App Router가 기본 라우팅 시스템으로 자리잡았습니다. 기존 Pages Router와 비교하여 더욱 직관적이고 강력한 기능을 제공하는 App Router에 대해 깊이 있게 알아보겠습니다.
App Router란 무엇인가
App Router는 Next.js 13에서 도입된 새로운 라우팅 시스템입니다. app 디렉토리를 기반으로 동작하며, React Server Components를 기본으로 사용합니다. 기존 pages 디렉토리 기반의 라우팅과는 근본적으로 다른 접근 방식을 취합니다.
기존 Pages Router에서는 모든 컴포넌트가 클라이언트에서 렌더링되거나 getServerSideProps, getStaticProps 같은 특수한 함수를 통해 서버 데이터를 가져왔습니다. 반면, App Router에서는 컴포넌트 자체가 서버에서 실행되므로 데이터 페칭이 훨씬 자연스럽습니다.
Pages Router와의 주요 차이점
| 특징 | Pages Router | App Router |
|---|---|---|
| 디렉토리 | pages/ | app/ |
| 기본 컴포넌트 | 클라이언트 컴포넌트 | 서버 컴포넌트 |
| 데이터 페칭 | getServerSideProps 등 | async 컴포넌트 직접 fetch |
| 레이아웃 | _app.tsx, _document.tsx | layout.tsx (중첩 가능) |
| 로딩 UI | 수동 구현 | loading.tsx 자동 지원 |
| 에러 처리 | _error.tsx | error.tsx (경로별 설정) |
파일 기반 라우팅 시스템
App Router의 라우팅은 app 디렉토리 내의 폴더 구조를 따릅니다. 각 폴더는 URL 경로의 세그먼트가 되며, 특별한 파일 이름으로 해당 경로의 동작을 정의합니다.
주요 파일 컨벤션
App Router에서 사용하는 특수 파일들은 다음과 같습니다.
- page.tsx: 해당 경로의 UI를 정의합니다. 이 파일이 있어야 해당 경로가 접근 가능합니다.
- layout.tsx: 하위 페이지들이 공유하는 레이아웃을 정의합니다. 페이지 전환 시 리렌더링되지 않습니다.
- loading.tsx: 페이지 로딩 중에 표시할 UI를 정의합니다. React Suspense를 자동으로 감싸줍니다.
- error.tsx: 에러 발생 시 표시할 UI를 정의합니다. Error Boundary를 자동으로 생성합니다.
- not-found.tsx: 404 페이지를 커스터마이징합니다.
- template.tsx: layout과 유사하지만, 네비게이션 시 새 인스턴스가 생성됩니다.
라우팅 예시
app/
├── page.tsx → /
├── about/
│ └── page.tsx → /about
├── blog/
│ ├── page.tsx → /blog
│ └── [slug]/
│ └── page.tsx → /blog/:slug
├── shop/
│ └── [...categories]/
│ └── page.tsx → /shop/* (catch-all)
└── (marketing)/
├── layout.tsx → 마케팅 그룹 레이아웃
└── campaigns/
└── page.tsx → /campaigns
서버 컴포넌트와 클라이언트 컴포넌트
App Router의 가장 큰 변화는 React Server Components(RSC)를 기본으로 사용한다는 점입니다. 서버 컴포넌트는 서버에서만 렌더링되며, JavaScript 번들에 포함되지 않아 성능상 큰 이점을 제공합니다.
서버 컴포넌트의 장점
서버 컴포넌트를 사용하면 여러 가지 이점이 있습니다. 첫째, 데이터베이스나 파일 시스템에 직접 접근할 수 있습니다. 둘째, 클라이언트로 전송되는 JavaScript 양이 줄어들어 초기 로딩 속도가 빨라집니다. 셋째, 민감한 정보(API 키, 데이터베이스 토큰 등)가 클라이언트에 노출되지 않습니다.
// 서버 컴포넌트 (기본값 - 별도 선언 불필요)
async function PostList() {
const posts = await fetch('https://api.example.com/posts');
const data = await posts.json();
return (
<ul>
{data.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
클라이언트 컴포넌트 사용
사용자 상호작용이 필요한 경우(클릭 이벤트, 상태 관리, 브라우저 API 사용 등)에는 클라이언트 컴포넌트를 사용합니다. 파일 상단에 "use client" 지시어를 추가하면 됩니다.
"use client";
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
}
컴포넌트 선택 기준
서버 컴포넌트와 클라이언트 컴포넌트를 선택할 때는 다음 기준을 참고하세요.
서버 컴포넌트를 사용하는 경우:
- 데이터 페칭이 주된 목적인 컴포넌트
- 백엔드 리소스에 직접 접근해야 하는 경우
- 민감한 정보를 서버에서만 처리해야 하는 경우
- 큰 의존성 패키지를 사용하는 경우 (클라이언트 번들 축소)
클라이언트 컴포넌트를 사용하는 경우:
- onClick, onChange 등 이벤트 리스너가 필요한 경우
- useState, useEffect 등 React 훅을 사용하는 경우
- 브라우저 전용 API를 사용하는 경우 (localStorage, window 등)
- 커스텀 훅이 상태나 효과에 의존하는 경우
데이터 페칭 전략
App Router에서의 데이터 페칭은 이전보다 훨씬 단순해졌습니다. 서버 컴포넌트에서 async/await를 직접 사용할 수 있기 때문입니다.
기본 데이터 페칭
async function getProducts() {
const res = await fetch('https://api.example.com/products', {
cache: 'force-cache', // 정적 데이터 (기본값)
});
return res.json();
}
export default async function ProductsPage() {
const products = await getProducts();return (
상품 목록
{products.map((product) => (
### 캐싱 전략
Next.js App Router에서는 `fetch` 함수에 캐싱 옵션을 지정할 수 있습니다.
- **`cache: 'force-cache'`**: 정적 데이터로 빌드 시 캐시됩니다. SSG와 유사합니다.
- **`cache: 'no-store'`**: 매 요청마다 새로 가져옵니다. SSR과 유사합니다.
- **`next: { revalidate: 60 }`**: 60초마다 데이터를 재검증합니다. ISR과 유사합니다.
## 레이아웃과 중첩 라우팅
App Router의 레이아웃 시스템은 매우 강력합니다. 레이아웃은 여러 페이지에 걸쳐 공유되며, 페이지 전환 시에도 상태를 유지합니다.
### 루트 레이아웃
// app/layout.tsx export default function RootLayout({ children, }: { children: React.ReactNode; }) { return (
### 중첩 레이아웃
대시보드 같은 복잡한 UI에서는 중첩 레이아웃이 유용합니다. 각 경로 세그먼트마다 독립적인 레이아웃을 가질 수 있으며, 이들이 자연스럽게 중첩됩니다.
// app/dashboard/layout.tsx export default function DashboardLayout({ children, }: { children: React.ReactNode; }) { return (
## 미들웨어와 인터셉팅 라우트
### 미들웨어 활용
미들웨어는 요청이 완료되기 전에 실행되는 코드입니다. 인증 확인, 리다이렉트, 요청 헤더 수정 등에 활용할 수 있습니다.
// middleware.ts import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) { const token = request.cookies.get('auth-token');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) { return NextResponse.redirect(new URL('/login', request.url)); }
return NextResponse.next(); }
export const config = { matcher: '/dashboard/:path*', };
### 병렬 라우트와 인터셉팅 라우트
App Router는 병렬 라우트(`@folder`)와 인터셉팅 라우트(`(.)folder`)를 지원합니다. 이를 통해 모달, 탭 UI 등 복잡한 라우팅 패턴을 구현할 수 있습니다.
## Server Actions
Server Actions는 서버에서 실행되는 비동기 함수로, 폼 제출이나 데이터 변경 작업을 처리하는 데 사용됩니다. 별도의 API 라우트를 만들지 않아도 됩니다.
// app/actions.ts "use server";
export async function createPost(formData: FormData) { const title = formData.get('title') as string; const content = formData.get('content') as string;
await db.post.create({ data: { title, content }, });
revalidatePath('/posts'); }
// app/posts/new/page.tsx import { createPost } from '../actions';
export default function NewPostPage() { return (
); }
## 마이그레이션 팁
기존 Pages Router 프로젝트를 App Router로 마이그레이션할 때 참고할 사항들입니다.
1. **점진적 마이그레이션**: `app`과 `pages` 디렉토리를 동시에 사용할 수 있으므로 페이지를 하나씩 옮기세요.
2. **데이터 페칭 변환**: `getServerSideProps`는 `cache: 'no-store'`로, `getStaticProps`는 기본 캐시로 대체합니다.
3. **메타데이터 마이그레이션**: `Head` 컴포넌트 대신 `metadata` 내보내기를 사용합니다.
4. **상태 관리 검토**: 서버 컴포넌트에서는 상태 관리 라이브러리가 불필요할 수 있으므로 재검토하세요.
## 마무리
Next.js App Router는 현대 웹 개발의 패러다임을 바꾸는 중요한 변화입니다. 서버 컴포넌트를 기본으로 사용하여 성능을 개선하고, 직관적인 파일 기반 라우팅으로 개발 경험을 향상시킵니다. 처음에는 낯설 수 있지만, 한번 익숙해지면 이전 방식으로 돌아가기 어려울 정도로 강력한 도구입니다.
App Router를 효과적으로 활용하기 위해서는 서버 컴포넌트와 클라이언트 컴포넌트의 경계를 명확히 이해하고, 적절한 캐싱 전략을 수립하는 것이 중요합니다. 꾸준히 공식 문서를 참고하면서 실전 프로젝트에 적용해 보시길 권장합니다.관련 게시글
JavaScript 비동기 프로그래밍 완벽 가이드
JavaScript의 비동기 프로그래밍을 콜백부터 Promise, async/await, 이벤트 루프, 에러 처리, 동시성 패턴까지 단계별로 완벽하게 정리합니다.
웹 성능 최적화 완벽 가이드: Core Web Vitals
Core Web Vitals를 중심으로 웹 성능을 측정하고 개선하는 방법을 알아봅니다. LCP, FID, CLS 지표와 실전 최적화 기법을 다룹니다.
Tailwind CSS 실전 활용법: 모던 웹 스타일링
Tailwind CSS를 활용한 효율적인 웹 스타일링 방법을 알아봅니다. 유틸리티 퍼스트 철학부터 반응형 디자인, 다크 모드, 컴포넌트 패턴까지 다룹니다.