app/ 디렉토리의 특수 파일들 — layout, page, loading, error
Next.js App Router에서 예약된 특수 파일 이름과 각 파일의 역할, 렌더링 계층 구조를 정리합니다.
지난 글에서 app/ 폴더 안에 어떤 파일이 있어야 URL로 매핑되는지 간략히 살펴봤습니다. 이번 글에서는 App Router가 인식하는 특수 파일 이름들을 하나씩 상세히 다룹니다.
특수 파일 목록
Next.js는 app/ 폴더 안에서 특정 이름을 가진 파일을 자동으로 인식합니다. 이름이 정해져 있고, 역할도 정해져 있습니다. 그 외 파일은 URL 라우팅에 관여하지 않습니다.
page.tsx — 라우트의 핵심
// app/about/page.tsx → /about URL
export default function AboutPage() {
return <h1>회사 소개</h1>;
}
page.tsx는 특정 URL 경로에 접근했을 때 렌더링되는 컴포넌트를 export합니다. 이 파일이 없으면 해당 폴더는 라우트로 처리되지 않습니다.
layout.tsx — 공유 래퍼
// app/layout.tsx — 루트 레이아웃
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ko">
<body>
<Header />
{children}
<Footer />
</body>
</html>
);
}
layout.tsx는 같은 폴더와 그 하위 모든 라우트를 감쌉니다. 라우트 이동 시에도 컴포넌트가 언마운트되지 않으므로 상태(예: 검색창 입력값)가 유지됩니다. 이것이 Pages Router의 _app.tsx와 다른 점입니다.
루트 app/layout.tsx는 반드시 <html>과 <body> 태그를 포함해야 합니다.
loading.tsx — 자동 Suspense 래핑
// app/dashboard/loading.tsx
export default function DashboardLoading() {
return <Skeleton />;
}
loading.tsx를 만들면 Next.js가 자동으로 <Suspense> 경계를 설정합니다. 같은 폴더의 page.tsx가 데이터를 패칭하는 동안 loading.tsx의 UI가 먼저 보입니다. 별도의 <Suspense> 코드 작성 없이도 스트리밍 렌더링이 가능한 이유입니다.
error.tsx — 에러 경계
// app/dashboard/error.tsx
'use client'; // 반드시 클라이언트 컴포넌트
export default function DashboardError({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
return (
<div>
<h2>오류가 발생했습니다.</h2>
<p>{error.message}</p>
<button onClick={reset}>다시 시도</button>
</div>
);
}
error.tsx는 반드시 'use client' 지시어가 있어야 합니다. reset 함수를 통해 에러 상태를 초기화하고 컴포넌트를 다시 렌더링할 수 있기 때문입니다. 서버에서는 이 상호작용이 불가능합니다.
not-found.tsx — 404 처리
// app/not-found.tsx
export default function NotFound() {
return <h1>페이지를 찾을 수 없습니다.</h1>;
}
서버 컴포넌트에서 notFound() 함수를 호출하거나, URL이 어떤 라우트와도 매칭되지 않을 때 표시됩니다.
import { notFound } from 'next/navigation';
export default async function PostPage({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug);
if (!post) notFound(); // not-found.tsx 렌더링
return <article>{post.content}</article>;
}
layout vs template
template.tsx는 layout.tsx와 구조가 동일하지만 동작이 다릅니다.
| layout.tsx | template.tsx | |
|---|---|---|
| 라우트 이동 시 | 언마운트 없이 유지 | 새 인스턴스 생성 |
| 상태 보존 | ✅ | ❌ |
| 사용 사례 | 내비게이션, 사이드바 | 입장 애니메이션, 조회수 트래킹 |
route.ts — API 엔드포인트
// app/api/users/route.ts
export async function GET(request: Request) {
const users = await db.user.findMany();
return Response.json(users);
}
export async function POST(request: Request) {
const body = await request.json();
const user = await db.user.create({ data: body });
return Response.json(user, { status: 201 });
}
route.ts는 HTTP 메서드 이름으로 함수를 export합니다. page.tsx와 같은 폴더에 공존할 수 없으니 주의하세요.
지난 글: Next.js 프로젝트 구조 완전 이해
다음 글: 파일 기반 라우팅 완전 정복
읽어주셔서 감사합니다. 😊