layout, loading, error, not-found 특수 파일로 화면을 구성한다.
레이아웃·로딩·에러 처리
App Router에는 폴더에 두기만 하면 자동으로 동작하는 특수 파일 들이 있습니다. 공통 레이아웃, 로딩 화면, 에러 화면, 404 화면을 코드 한 줄 연결 없이 만들 수 있습니다. 이번 레슨에서 핵심 네 가지를 익힙니다.
학습 목표
layout.tsx로 공통 UI를 만들고 중첩한다.template.tsx와 layout의 차이를 안다.loading.tsx로 Suspense 기반 로딩 화면을 만든다.error.tsx와not-found.tsx로 오류를 우아하게 처리한다.
layout.tsx — 공통 레이아웃
layout.tsx 는 하위 페이지들을 감싸는 껍데기입니다. 루트 레이아웃은 <html>, <body> 를 포함하며 반드시 있어야 합니다. children 으로 페이지가 들어옵니다.
// app/layout.tsx — 루트 레이아웃
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ko">
<body>
<header>사이트 헤더</header>
{children}
<footer>© 2026</footer>
</body>
</html>
);
}
레이아웃은 중첩됩니다. 폴더마다 layout.tsx 를 두면 바깥 레이아웃 안에 안쪽 레이아웃이 끼워집니다.
app/
├── layout.tsx → 전체 껍데기
└── dashboard/
├── layout.tsx → 대시보드 전용 사이드바
└── page.tsx
💡 TIP — 레이아웃은 페이지를 이동해도 다시 렌더되지 않고 상태를 유지합니다. 사이드바의 스크롤 위치가 페이지 전환에도 보존되는 이유입니다.
template.tsx — 매번 새로 그리는 껍데기
template.tsx 는 레이아웃과 거의 같지만, 페이지를 이동할 때마다 새 인스턴스로 다시 마운트됩니다. 페이지 전환 애니메이션이나 진입 시마다 초기화가 필요할 때 씁니다.
| 파일 | 이동 시 동작 | 상태 |
|---|---|---|
layout.tsx | 유지(재렌더 안 함) | 보존 |
template.tsx | 매번 다시 마운트 | 초기화 |
loading.tsx — 로딩 화면
loading.tsx 를 두면 Next.js가 해당 세그먼트를 자동으로 <Suspense> 로 감쌉니다. 서버 컴포넌트가 데이터를 기다리는 동안 이 화면이 먼저 보입니다.
// app/posts/loading.tsx
export default function Loading() {
return <p>글을 불러오는 중...</p>;
}
같은 폴더의 page.tsx 가 async 로 데이터를 기다리면, 그동안 자동으로 loading.tsx 가 표시됩니다. 별도 상태 관리가 필요 없습니다.
error.tsx — 에러 경계
렌더 중 에러가 나면 error.tsx 가 그 자리에 표시됩니다. 에러 경계는 클라이언트 컴포넌트 여야 하므로 'use client' 가 필요합니다.
'use client';
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
return (
<div>
<p>문제가 발생했어요: {error.message}</p>
<button onClick={() => reset()}>다시 시도</button>
</div>
);
}
reset 을 호출하면 해당 세그먼트를 다시 렌더링하려 시도합니다.
⚠️ 주의 —
error.tsx는 같은 세그먼트의layout.tsx에서 난 에러는 잡지 못합니다. 그 경우 한 단계 위(상위 폴더)의error.tsx가 처리합니다.
not-found.tsx — 404 화면
notFound() 함수를 호출하면 가장 가까운 not-found.tsx 가 렌더됩니다. 데이터가 없을 때 깔끔하게 404를 보여줄 수 있습니다.
// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';
export default async function Post({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPost(slug);
if (!post) notFound(); // → not-found.tsx 렌더
return <article>{post.title}</article>;
}
// app/blog/[slug]/not-found.tsx
export default function NotFound() {
return <p>찾는 글이 없어요.</p>;
}
요약
layout.tsx는 중첩되는 공통 껍데기이며 이동해도 상태를 유지한다.template.tsx는 이동마다 다시 마운트되는 껍데기다.loading.tsx는 자동 Suspense 로딩 화면을 만든다.error.tsx(클라이언트)와not-found.tsx로 오류와 404를 처리한다.
연습문제
/dashboard에 사이드바가 있는 전용layout.tsx를 만들어 보세요.- 데이터를 1초 지연시켜 패칭하는 페이지에
loading.tsx를 추가하고, 로딩 화면이 보이는지 확인하세요. - 일부러 에러를 던지는 컴포넌트를 만들고
error.tsx로 잡아 "다시 시도" 버튼을 붙여 보세요. - 존재하지 않는 글 slug일 때
notFound()를 호출하도록 만들어 보세요.
힌트 —
error.tsx는 반드시'use client'로 시작해야 합니다.loading.tsx는 같은 폴더의async page가 데이터를 기다릴 때만 보입니다.
💡 연습문제 풀이
불러오는 중…
댓글 0
“Next.js” 강좌에 대한 댓글입니다.