Ch 03. 페이지 안의 페이지 — 중첩 레이아웃
러시아 인형처럼
러시아 전통 인형 마트료시카를 아시나요. 큰 인형 안에 작은 인형이 있고, 그 안에 더 작은 인형이 있습니다. Next.js의 레이아웃이 꼭 그렇습니다.
루트 레이아웃 안에 섹션 레이아웃이 있고, 섹션 레이아웃 안에 페이지가 있습니다. 바깥 레이아웃은 고정되어 있고, 안쪽 레이아웃도 고정되어 있으며, 가장 안쪽의 페이지만 바뀝니다.
중첩 레이아웃이 필요한 상황
쇼핑몰을 만든다고 가정합니다. 모든 페이지에는 공통 헤더가 있습니다. 그런데 관리자 페이지(/dashboard)에는 추가로 사이드바가 있습니다. 일반 사용자 페이지에는 사이드바가 없습니다.
이런 경우 루트 레이아웃과 대시보드 레이아웃을 분리합니다.
app/├── layout.tsx ← 루트 레이아웃 (헤더 포함)├── page.tsx ← /├── dashboard/│ ├── layout.tsx ← 대시보드 레이아웃 (사이드바 포함)│ ├── page.tsx ← /dashboard│ ├── products/│ │ └── page.tsx ← /dashboard/products│ └── orders/│ └── page.tsx ← /dashboard/orders
대시보드 레이아웃 만들기
// 파일: app/dashboard/layout.tsx
import Link from 'next/link';
const navItems = [
{ href: '/dashboard', label: '개요' },
{ href: '/dashboard/products', label: '상품 관리' },
{ href: '/dashboard/orders', label: '주문 관리' },
];
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div style={{ display: 'flex' }}>
<aside style={{ width: '240px', borderRight: '1px solid #eee' }}>
<nav>
<ul>
{navItems.map((item) => (
<li key={item.href}>
<Link href={item.href}>{item.label}</Link>
</li>
))}
</ul>
</nav>
</aside>
<section style={{ flex: 1, padding: '24px' }}>{children}</section>
</div>
);
}
```text
이제 `/dashboard`, `/dashboard/products`, `/dashboard/orders`는 모두 이 레이아웃 안에서 렌더링됩니다. 루트 레이아웃의 헤더 + 대시보드 레이아웃의 사이드바 + 각 페이지의 내용, 세 겹이 쌓입니다.
### 레이아웃이 공유되고 페이지만 바뀌는 원리
`/dashboard/products`에서 `/dashboard/orders`로 이동할 때, Next.js는 두 레이아웃을 다시 렌더링하지 않습니다. 루트 레이아웃과 대시보드 레이아웃은 그대로 유지됩니다. `children`에 해당하는 페이지 컴포넌트만 교체됩니다.
이 덕분에 페이지 이동이 매우 빠르고, 사이드바의 스크롤 위치나 상태가 유지됩니다. 레이아웃 컴포넌트는 마운트/언마운트 없이 계속 살아있기 때문입니다.
### 각 페이지 파일
```tsx
// 파일: app/dashboard/page.tsx
export default function DashboardPage() {
return (
<div>
<h1>대시보드 개요</h1>
<p>오늘의 판매 현황을 확인하세요.</p>
</div>
);
}
```text
```tsx
// 파일: app/dashboard/products/page.tsx
export default function ProductsPage() {
return (
<div>
<h1>상품 관리</h1>
<p>등록된 상품 목록입니다.</p>
</div>
);
}
다음 챕터에서는
다음 챕터에서는 이름을 모르는 경로를 처리하는 방법을 다룹니다. 블로그 게시글 주소처럼 /posts/my-first-article, /posts/nextjs-tutorial 같이 경로의 일부가 동적으로 바뀌는 경우, 어떻게 처리하는지 살펴봅니다.