Next.js App Router는 app/ 디렉토리의 폴더 구조를 URL로 그대로 변환한다. 별도의 라우터 설정 없이 파일을 만드는 것만으로 페이지가 생긴다. 이 단순한 규칙 안에 정적 라우트부터 Parallel Routes까지 강력한 패턴이 숨어 있다.
app/ 아래에 폴더를 만들고 page.tsx를 두면 해당 경로가 URL로 등록된다.
app/
├── page.tsx → /
├── about/
│ └── page.tsx → /about
└── blog/
├── page.tsx → /blog
└── recent/
└── page.tsx → /blog/recent
폴더가 중첩될수록 URL 세그먼트도 함께 중첩된다. layout.tsx를 각 폴더에 두면 해당 세그먼트 이하 모든 페이지가 공통 레이아웃을 공유한다.
[slug]폴더 이름을 대괄호로 감싸면 Dynamic Segment가 된다. 런타임에 실제 값이 들어온다.
// app/blog/[slug]/page.tsx
export default function BlogPost({
params,
}: {
params: { slug: string };
}) {
return <h1>{params.slug}</h1>;
}
Dynamic Segment — URL의 가변 부분. [slug]는 /blog/hello-world에서 slug = "hello-world"로 바인딩된다.
[[...slug]]처럼 이중 대괄호와 ...를 조합하면 Optional Catch-all Segment가 되어 0개 이상의 세그먼트를 배열로 받는다.
(group)폴더 이름을 소괄호로 감싸면 URL에는 포함되지 않는 Route Group이 된다. 레이아웃을 분리하거나 파일을 논리적으로 묶을 때 사용한다.
app/
├── (marketing)/
│ ├── layout.tsx ← 마케팅 전용 레이아웃
│ ├── page.tsx → /
│ └── about/
│ └── page.tsx → /about
└── (shop)/
├── layout.tsx ← 쇼핑 전용 레이아웃
└── cart/
└── page.tsx → /cart
Route Group — URL 경로에 영향을 주지 않고 파일 시스템을 그루핑하는 메커니즘. (marketing)은 URL에 나타나지 않는다.
Parallel Routes는 하나의 레이아웃 안에서 여러 페이지를 동시에 렌더링한다. 폴더 이름 앞에 @를 붙여 Slot을 정의한다.
app/
└── dashboard/
├── layout.tsx
├── page.tsx
├── @analytics/
│ └── page.tsx
└── @team/
└── page.tsx
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
analytics,
team,
}: {
children: React.ReactNode;
analytics: React.ReactNode;
team: React.ReactNode;
}) {
return (
<div>
{children}
{analytics}
{team}
</div>
);
}
Slot — @folderName 형태로 선언하는 Parallel Routes의 단위. 부모 layout.tsx의 props로 주입된다.
Intercepting Routes는 클라이언트 내비게이션 시 다른 경로를 "가로채" 현재 컨텍스트 안에서 렌더링한다. 모달 패턴에 자주 쓰인다.
| 규칙 | 의미 |
|---|---|
(.)path | 같은 레벨 세그먼트 가로채기 |
(..)path | 한 레벨 위 세그먼트 가로채기 |
(...)path | root 기준 가로채기 |
피드에서 사진을 클릭하면 URL은 /photos/42로 바뀌지만 현재 페이지 위에 모달로 사진이 열리고, 새로고침하면 전체 페이지로 이동하는 패턴이 대표적이다.