Next.js App Router
Next.js 14+ 및 app/ 디렉토리와 TypeScript에 적용됩니다.
파일 규칙
app/layout.tsx— 필수 루트 레이아웃.<html>과<body>를 포함해야 합니다.app/page.tsx— 라우트 진입점. 기본적으로 React Server Component(RSC)입니다.app/loading.tsx— 세그먼트에 대한 스트리밍 폴백입니다.app/error.tsx— 클라이언트 컴포넌트('use client')여야 하며,{ error, reset }을 받습니다.app/not-found.tsx— 404 UI입니다.app/route.ts— 라우트 핸들러(API).GET,POST등을 내보냅니다.- 동적 라우트:
app/blog/[slug]/page.tsx는{ params: { slug: string } }을 받습니다. - 그룹:
app/(marketing)/about/page.tsx— URL에 영향을 주지 않습니다. - 병렬:
app/@modal/page.tsx+layout({ modal }). 인터셉팅:app/(.)photo/[id]/page.tsx.
규칙
- 기본적으로 서버 컴포넌트입니다. 훅, 이벤트 또는 브라우저 API가 필요한 경우에만
'use client'를 추가하세요. 클라이언트 경계를 작게 유지하고 리프(leaf) 컴포넌트로 밀어 넣으세요. params와searchParams는 Next 15에서 비동기(async)입니다:await params.- 서버 전용 기능을 사용하는 서버 컴포넌트를 클라이언트 컴포넌트에 직접 가져오지 마세요. 대신
children/props로 전달하세요. - 네비게이션에는
next/link를 사용하고,next/navigation(useRouter,usePathname)은 클라이언트 컴포넌트에서만 사용하세요. 서버 컴포넌트에서는next/navigation의redirect()/notFound()를 사용하세요. - 파일 배치: 세그먼트별로
page.tsx,loading.tsx,error.tsx를 둡니다. 공유 UI는 라우팅되지 않는 한app/이 아닌components/에 둡니다.
패턴
// app/blog/[slug]/page.tsx — Next 15 async params
type Props = { params: Promise<{ slug: string }> };
export default async function BlogPost({ params }: Props) {
const { slug } = await params;
const post = await getPost(slug);
if (!post) notFound();
return <article>{post.title}</article>;
}
export async function generateStaticParams() {
const slugs = await getAllSlugs();
return slugs.map((slug) => ({ slug }));
}
// app/error.tsx — 반드시 Client Component여야 함
'use client';
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
return (
<div>
<h2>문제가 발생했습니다</h2>
<button onClick={() => reset()}>다시 시도</button>
</div>
);
}
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
체크리스트
- 올바른 파일 이름과 세그먼트 위치인가요?
- 필요한 곳에만 Client 지시어를 사용했나요?
- 비동기 세그먼트에
loading.tsx/error.tsx가 있나요? - SEO가 중요한 경우
generateStaticParams+generateMetadata를 사용했나요? -
next/router(pages-router 임포트)의useRouter를 사용하지 않았나요?