일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- CCM
- 설교
- 한동대학교
- FE
- SQLD
- 유태준교수님
- 글로벌리더십학부
- 어노인팅
- 날마다 솟는 샘물
- 묵상
- CHEMISTRY
- Database
- QT
- 찬양
- 네트워킹
- 프론트엔드
- 예배
- csee
- 고윤민교수님
- 날솟샘
- GLS
- dbms
- 데이터베이스
- 컴네
- SQL
- 화학
- 일반화학
- computer networks and the internet
- 전산전자공학부
- 혼자공부하는sql
- Today
- Total
멈추지 않는 기록
[Next.js] pages는 이제 안녕? App Router가 가져온 구조 혁신 본문
“Next.js를 배우고 있는데… /app 디렉토리?
_app.tsx, _document.tsx는 이제 안 쓰는 건가요?”
Next.js 13부터 등장한 App Router는 단순한 디렉토리 변경이 아니다.
파일 기반 라우팅 → 컴포넌트 기반 아키텍처로, Next.js의 구조 자체가 진화한 것이다.
이 포스팅에서는 App Router의 탄생 배경부터 핵심 구조, 그리고 기존 Pages Router와의 차이까지 한 번에 정리해보겠다.
App Router는 왜 생겼을까?
Next.js는 오랫동안 pages/ 디렉토리를 통해 파일 기반 라우팅을 제공해왔다.
간편하고 직관적이지만, 한계도 명확했다.
- 페이지마다 공통 레이아웃을 구성하기가 복잡했다 (_app.tsx, 조건부 렌더링 등)
- 코드 스플리팅, 로딩 상태 관리가 번거로웠다
- 서버 컴포넌트를 효율적으로 다룰 수 없었다
이런 문제를 해결하고 React Server Component(RSC)를 도입하기 위해,
Next.js 13부터 새로운 라우팅 구조인 App Router가 도입되었다.
✅ 핵심 요약:
더 유연한 레이아웃 구성, 더 나은 서버 렌더링, 더 구조적인 UX 관리를 위해 App Router가 등장했다.
App Router의 기본 구조 – /app 디렉토리
App Router는 pages/ 대신 app/ 디렉토리를 사용한다.
그리고 페이지 단위가 아닌, 컴포넌트 트리 기반의 구조를 따른다.
/app
layout.tsx # 전체 레이아웃
page.tsx # 현재 경로의 콘텐츠
loading.tsx # 로딩 중 UI
error.tsx # 에러 UI
template.tsx # layout을 재사용 가능한 템플릿으로 분리
/dashboard
layout.tsx
page.tsx
✔️ 주요 구성 요소 설명
파일 | 역할 |
layout.tsx | 중첩 가능한 공통 레이아웃 구성 |
page.tsx | 해당 경로의 메인 콘텐츠 |
loading.tsx | 데이터 로딩 중에 보여줄 UI |
error.tsx | 렌더링 오류 시 fallback UI |
template.tsx | layout과 비슷하지만 매 진입마다 초기화되는 구조 |
이처럼 App Router는 파일을 기반으로 하는 것이 아니라, 구조를 명시적으로 나누는 방식이다.
마치 프레임워크와 디자인 시스템이 결합된 느낌이다.
서버 컴포넌트 vs 클라이언트 컴포넌트
App Router에서는 모든 컴포넌트가 기본적으로 "서버 컴포넌트(Server Component)"이다.
// 기본은 서버 컴포넌트
export default function Page() {
const data = await fetch(...); // 가능!
return <div>{data}</div>;
}
하지만 클라이언트 상태나 훅(useState, useEffect 등)이 필요한 경우, 다음처럼 선언해야 한다:
'use client'
import { useState } from 'react';
export default function ClientComponent() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
✔️ 이 구조의 장점
- 서버에서 직접 DB나 API를 호출해 HTML을 미리 만들 수 있다 (SEO 👍, 속도 👍)
- 클라이언트에 불필요한 JS 코드를 보내지 않는다 (퍼포먼스 향상)
- 필요한 부분만 클라이언트 컴포넌트로 구성할 수 있다 (정밀 제어)
기존 Pages Router와의 비교
항목 | Pages Router (pages/) | App Router (app/) |
기본 디렉토리 | pages/ | app/ |
레이아웃 구성 | _app.tsx, _document.tsx | layout.tsx, template.tsx |
HTML 구조 설정 | _document.tsx | app/html.tsx |
상태 관리 | CSR 기반 (클라이언트 중심) | 기본 SSR + 클라이언트 분리 |
데이터 패칭 | getStaticProps, getServerSideProps | async 서버 컴포넌트 사용 |
로딩/에러 UI | 수동 처리 | loading.tsx, error.tsx 자동 지원 |
App Router는 기존 구조를 대체하는 것이 아니라,
더 나은 아키텍처를 위한 새로운 선택지이다.
실제 예시로 보는 App Router
App Router를 사용할 때 가장 핵심적인 구조는 app/ 디렉토리 안에 다음과 같은 파일들을 배치하는 것이다.
/app
layout.tsx
page.tsx
loading.tsx
error.tsx
이 구조를 기준으로, App Router의 렌더링 흐름을 하나씩 따라가 보자.
✅ 1. layout.tsx – 공통 레이아웃 담당
// app/layout.tsx
import "./globals.css";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<body>
<header>헤더 들어갈 자리</header>
{children}
<footer>푸터 들어갈 자리</footer>
</body>
</html>
);
}
- 모든 페이지를 감싸는 최상위 레이아웃
- children에 각 경로의 페이지 내용이 들어온다
- html, body 태그까지 구성할 수 있기 때문에 _document.tsx의 역할을 어느 정도 대체함
- 페이지 전환 시 레이아웃은 재사용된다 (초기화되지 않음)
✅ 2. page.tsx – 현재 경로의 메인 콘텐츠
// app/page.tsx
export default async function Home() {
const res = await fetch("https://api.example.com/news", { cache: "no-store" });
const news = await res.json();
return (
<main>
<h1>오늘의 뉴스</h1>
<ul>
{news.map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
</main>
);
}
- 라우팅의 끝점이 되는 페이지 컴포넌트
- 기본적으로 서버 컴포넌트이기 때문에, async/await로 서버에서 직접 데이터 패칭이 가능
- 클라이언트에 데이터를 넘기기 전 HTML로 미리 만들어지므로 SEO와 초기 로딩 속도에 매우 유리
💡 useEffect로 API 요청을 하지 않아도 되는 구조이기 때문에, 페이지가 처음부터 데이터가 채워진 상태로 사용자에게 보여진다.
✅ 3. loading.tsx – 데이터 패칭 중 표시되는 UI
// app/loading.tsx
export default function Loading() {
return <p>불러오는 중입니다...</p>;
}
- 해당 경로에 있는 서버 컴포넌트가 비동기로 데이터를 불러오는 동안 자동으로 보여지는 fallback UI
- 각 페이지나 layout 경로에 맞춰 슬롯 방식으로 사용 가능
/app/dashboard
page.tsx
loading.tsx ← 대시보드 전용 로딩 화면
예전에는 useState, isLoading 등을 조합해서 수동으로 처리했지만,
이제는 디렉토리 기반으로 자동 연결된다. UI 구성과 상태 관리가 분리되어 훨씬 명확하다.
✅ 4. error.tsx – 렌더링 오류 시 fallback UI
// app/error.tsx
"use client";
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
return (
<div>
<h2>문제가 발생했습니다!</h2>
<button onClick={() => reset()}>다시 시도</button>
</div>
);
}
- 특정 경로에서 컴포넌트 렌더링 중 에러가 발생하면 자동으로 이 컴포넌트가 보여진다
- *에러 경계(Error Boundary)**를 컴포넌트 단위로 만들 필요 없이 자동 처리
- reset() 함수를 통해 오류를 초기화하고 다시 시도할 수 있음
🔁 전체 흐름 요약
사용자가 / 로 접근
→ layout.tsx 렌더링
→ page.tsx에서 async fetch 요청
→ 로딩 중에는 loading.tsx 표시
→ fetch 완료 후 실제 콘텐츠 렌더링
→ 렌더링 중 오류 발생 시 error.tsx 표시
🔍 핵심 요약
- App Router는 서버 중심 구조 + 구조적 UI 관리 + 성능 최적화를 위해 등장했다.
- app/ 디렉토리 기반으로, 레이아웃, 로딩, 에러까지 컴포넌트 단위로 나눈다.
- 모든 컴포넌트는 기본 서버 컴포넌트이며, 필요할 때만 use client를 사용한다.
- 더 깔끔한 코드 설계, 더 빠른 초기 로딩, 더 나은 SEO 성능을 제공한다.
🧩 마무리하며
App Router는 단순한 디렉토리 변경이 아니라, Next.js가 컴포넌트 중심 구조로 진화했다는 증거다.
layout.tsx, page.tsx, loading.tsx, error.tsx처럼 역할이 나뉜 구조는 코드를 더 명확하게 설계할 수 있게 도와주고,
서버 컴포넌트 기반의 데이터 패칭은 SEO와 성능 모두에 유리하다.
처음에는 복잡해 보일 수 있지만, 프로젝트 규모가 커질수록 진가를 발휘하는 구조다.
공통 레이아웃, 상태 관리, 로딩/에러 처리 등 모든 흐름이 더 깔끔하게 정리된다.
이제는 pages 방식에서 한 단계 나아가, App Router의 흐름과 철학을 이해할 때다.
조금씩 익혀두면, 앞으로의 웹 개발에 분명 강력한 무기가 될 것이다.
'개발 > Next.js' 카테고리의 다른 글
[Next.js] 실무에서 쓰는 Next.js 구조 설계 – src, app, features, components (1) | 2025.07.01 |
---|---|
[Next.js] Next.js + TypeScript 기반 프로젝트 시작 가이드 (0) | 2025.06.25 |
[Next.js] _app.tsx와 _document.tsx 완전 이해하기 (0) | 2025.04.16 |
[Next.js] getStaticProps와 getServerSideProps, Next.js가 데이터에 진심인 이유 (0) | 2025.04.16 |
[Next.js] React는 왜 SEO에 약할까? Next.js가 해답이다 (0) | 2025.04.16 |