유리의 개발새발
[Next] 13.App Router 본문
페이지 라우터(Page Router)랑 너무 다른데요...?
아니... 하... 뭐 어쩌겠어요. 시대의 흐름은 따라가야죠. 해야죠.
이번에는 App Router 기반으로 프로젝트를 만들어보겠습니다.
먼저 생성된 디렉토리 구조를 확인하고, 각 폴더가 어떤 역할을 하는지 간단히 설명드릴게요.
npx create-next-app@latest {projectName}
구성부터 보져.
✅ app/ 디렉토리
App Router의 중심이 되는 디렉토리입니다.
pages/ 대신 app/ 폴더를 루트로 사용하며, 여기에 URL 경로를 기준으로 폴더와 파일을 구성합니다.
라우팅, 레이아웃, 에러 핸들링까지 전부 여기서 처리됩니다.
✅ layout.tsx
각 경로마다 공통으로 유지할 UI, 예를 들면 헤더, 사이드바, 푸터 같은 것들을 여기에 작성합니다.
✅ page.tsx
해당 경로의 실제 페이지입니다.
App Router에서는 이 파일 하나가 하나의 라우트 역할을 합니다.
예전 pages/index.tsx, pages/about.tsx 이런 거랑 비슷하지만, 더 유연해졌어요.
✅ loading.tsx
비동기 로딩이 걸리는 페이지나 레이아웃에서 자동으로 보여줄 로딩 UI를 여기에 작성합니다.
별도로 상태 관리할 필요 없이, Next.js가 알아서 이 파일을 보여줍니다.
✅ error.tsx
해당 경로에서 렌더링 중 에러가 발생했을 때 보여줄 UI입니다.
컴포넌트 내에서 throw되든, fetch 중 에러가 나든, Next가 자동으로 이걸 호출합니다.
📌 페이지 라우팅 설정
기존 Page Router에서는 pages/ 디렉토리 안에 있는 파일명이 곧 URL 경로가 되었죠?
pages/index.tsx → localhost:3000/
pages/detail.tsx → localhost:3000/detail
pages/blog/[id].tsx → localhost:3000/blog/123
그런데 App Router에서는?
App Router에서는 조금 다릅니다.
"무조건 page.tsx라는 파일만이 진짜 페이지로 인식"됩니다.
app/
├── page.tsx → localhost:3000/
├── detail/
│ └── page.tsx → localhost:3000/detail
├── blog/
│ └── [id]/
│ └── page.tsx → localhost:3000/blog/123
📌 핵심 차이점
- Page Router: 파일명 자체가 경로
- App Router: 폴더명 = 경로, page.tsx = 그 경로의 페이지

자 여기서, 이상한 점.
왜 async await가 있는가?
🧠 왜 async가 가능할까?
✅ App Router의 page.tsx는 서버 컴포넌트입니다.
Next.js 13 이상, App Router에서는 기본적으로 모든 컴포넌트가 서버 컴포넌트(Server Component)로 동작합니다.
- 서버 컴포넌트는 브라우저가 아닌 서버에서 실행되므로,
fetch, await, DB 쿼리, 파일 I/O 같은 비동기 작업을 컴포넌트 안에서 직접 수행할 수 있습니다. - 서버 컴포넌트에서는 useEffect, useState 같은 클라이언트 훅은 사용 불가능하지만,
대신 비동기 처리는 자유롭게 가능합니다.
App Router의 page.tsx, layout.tsx, error.tsx, loading.tsx 등은 서버에서 렌더링되는 함수로 동작하며, async 함수로 정의할 수 있습니다.
아직 잘 와닿지 않는데요, 일단은 넘어가고 추 후에 다시 살펴보죠.
📌 페이지 레이아웃 설정 (App Router 기준)
App Router에서는 layout.tsx 파일을 사용해서 페이지마다 공통 UI 구조(레이아웃)를 설정할 수 있습니다.
예전 Page Router에서는 _app.tsx나 _document.tsx에서 전체 앱의 공통 구조를 관리했지만,
이제는 경로별로 쪼갠 레이아웃을 계층적으로 구성할 수 있게 바뀌었습니다.
app/
├── layout.tsx ← 모든 페이지의 최상위 레이아웃
├── page.tsx ← /
├── about/
│ ├── layout.tsx ← /about 전용 레이아웃
│ └── page.tsx ← /about
├── dashboard/
│ ├── layout.tsx ← /dashboard 전용 레이아웃
│ ├── page.tsx ← /dashboard
│ └── analytics/
│ └── page.tsx ← /dashboard/analytics
- layout.tsx는 React 컴포넌트처럼 보이지만, 반드시 children을 렌더링해야 작동합니다.
- layout.tsx는 계층적으로 중첩되며, 해당 폴더의 모든 하위 페이지에 공통 적용됩니다.
// app/layout.tsx
import "./globals.css";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<body>
<Header />
<main>{children}</main>
<Footer />
</body>
</html>
);
}

- /search 아래에 있는 모든 페이지는 app/layout.tsx(루트 레이아웃)의 영향을 반드시 받습니다.
- /search/layout.tsx가 있다면?
→ 루트 레이아웃 + /search 레이아웃 둘 다 적용됩니다. - /search/result/page.tsx 같은 하위 페이지는?
→ 루트 → search → result 순서대로 레이아웃이 중첩됨 (result에 layout이 없으면 위 두 개만)
약간 상속 느낌이군요?

음 퍼블리싱 좀 빡셀지도...?

근데, 이거 중첩이라고 했잖아요.
예를 들면 /search/setting를 만들었을 때에도 적용이 되는거죠.
그리고 /search/setting에서 layout.tsx 만들어서 또 뭘 꾸미면, 그것도 중첩이 되잖아요.
📌 공통되지 않은 경로에서 공통된 레이아웃을 적용하고 싶다면?
예를 들어 /search, /profile, /dashboard는 URL이 서로 다르지만,
이들 모두에 같은 레이아웃을 적용하고 싶은 경우가 있겠죠?
이럴 땐 Route Group을 쓰면 됩니다.
app/
├── (dashboard-group)/
│ ├── layout.tsx ← 공통 레이아웃
│ ├── search/
│ │ └── page.tsx → /search
│ ├── profile/
│ │ └── page.tsx → /profile
│ └── dashboard/
│ └── page.tsx → /dashboard
- /search, /profile, /dashboard는 각기 다른 URL을 갖지만
- (dashboard-group)/layout.tsx는 세 경로 모두에 공통 적용됩니다.
즉, 라우트 URL은 건드리지 않고, 공통 UI만 묶어서 관리할 수 있게 되는 것이죠.

이러면 ()디렉토리에 있는 layout이 공통으로 적용 되겠져? 물론, url을 바꾸지 않으면서!
'Next' 카테고리의 다른 글
| [Next] 15. App Router에서 데이터 패칭 (0) | 2025.08.02 |
|---|---|
| [Next] 14. React Server Component (1) | 2025.08.01 |
| [Next] 12. Page Router (1) | 2025.07.31 |
| [Next] 11. 배포 (2) | 2025.07.31 |
| [Next] 10. SEO (3) | 2025.07.31 |