시작하기: 레이아웃과 페이지
시작하기: 레이아웃과 페이지 | Next.js
섹션 제목: “시작하기: 레이아웃과 페이지 | Next.js”원본 URL: https://nextjs.org/docs/app/getting-started/layouts-and-pages
App RouterGetting StartedLayouts and Pages
레이아웃과 페이지
섹션 제목: “레이아웃과 페이지”마지막 업데이트 2026년 2월 20일
Next.js는 파일 시스템 기반 라우팅 을 사용하므로 폴더와 파일로 라우트를 정의할 수 있습니다. 이 문서는 레이아웃과 페이지를 만드는 방법과 서로 연결하는 방법을 안내합니다.
페이지 만들기
섹션 제목: “페이지 만들기”페이지 는 특정 라우트에서 렌더링되는 UI입니다. 페이지를 만들려면 app 디렉터리 안에 page 파일을 추가하고 React 컴포넌트를 기본 내보내기(default export)하세요. 예를 들어 인덱스 페이지(/)를 만들려면 다음과 같이 합니다:
app/page.tsx
JavaScriptTypeScript
export default function Page() { return <h1>Hello Next.js!</h1> }레이아웃 만들기
섹션 제목: “레이아웃 만들기”레이아웃은 여러 페이지 간에 공유되는 UI입니다. 네비게이션 시 레이아웃은 상태를 유지하고, 상호작용성을 잃지 않으며, 다시 렌더링되지 않습니다.
layout 파일에서 React 컴포넌트를 기본 내보내면 레이아웃을 정의할 수 있습니다. 해당 컴포넌트는 페이지나 다른 레이아웃이 될 수 있는 children prop을 받아야 합니다.
예를 들어 인덱스 페이지를 자식으로 받는 레이아웃을 만들려면 app 디렉터리에 layout 파일을 추가합니다:
app/layout.tsx
JavaScriptTypeScript
export default function DashboardLayout({ children, }: { children: React.ReactNode }) { return ( <html lang="en"> <body> {/* Layout UI */} {/* Place children where you want to render a page or nested layout */} <main>{children}</main> </body> </html> ) }위 레이아웃은 app 디렉터리의 루트에 정의되어 있기 때문에 루트 레이아웃이라고 합니다. 루트 레이아웃은 필수 이며 html 및 body 태그를 포함해야 합니다.
중첩 라우트 만들기
섹션 제목: “중첩 라우트 만들기”중첩 라우트는 여러 URL 세그먼트로 구성된 라우트입니다. 예를 들어 /blog/[slug] 라우트는 다음 세 세그먼트로 구성됩니다:
/(루트 세그먼트)blog(세그먼트)[slug](리프 세그먼트)
Next.js에서는:
- 폴더 를 사용해 URL 세그먼트에 매핑되는 라우트 세그먼트를 정의합니다.
page나layout처럼 파일 을 사용해 해당 세그먼트에 표시되는 UI를 만듭니다.
중첩 라우트를 만들려면 폴더를 서로 중첩하면 됩니다. 예를 들어 /blog 라우트를 추가하려면 app 디렉터리에 blog라는 폴더를 만듭니다. 그런 다음 /blog를 공개하려면 page.tsx 파일을 추가합니다:
app/blog/page.tsx
JavaScriptTypeScript
// Dummy imports import { getPosts } from '@/lib/posts' import { Post } from '@/ui/post'
export default async function Page() { const posts = await getPosts()
return ( <ul> {posts.map((post) => ( <Post key={post.id} post={post} /> ))} </ul> ) }폴더를 계속 중첩해 중첩 라우트를 만들 수 있습니다. 예를 들어 특정 블로그 게시물용 라우트를 만들려면 blog 안에 새로운 [slug] 폴더를 만들고 page 파일을 추가합니다:
app/blog/[slug]/page.tsx
JavaScriptTypeScript
function generateStaticParams() {}
export default function Page() { return <h1>Hello, Blog Post Page!</h1> }폴더 이름을 대괄호(예: [slug])로 감싸면 동적 라우트 세그먼트가 생성되어 데이터로부터 여러 페이지를 만들 수 있습니다. 예: 블로그 게시물, 제품 페이지 등.
레이아웃 중첩하기
섹션 제목: “레이아웃 중첩하기”기본적으로 폴더 계층의 레이아웃은 중첩되어 children prop을 통해 하위 레이아웃을 감쌉니다. 특정 라우트 세그먼트(폴더)에 layout을 추가해 레이아웃을 중첩할 수 있습니다.
예를 들어 /blog 라우트의 레이아웃을 만들려면 blog 폴더 안에 새로운 layout 파일을 추가합니다.
app/blog/layout.tsx
JavaScriptTypeScript
export default function BlogLayout({ children, }: { children: React.ReactNode }) { return <section>{children}</section> }위 두 레이아웃을 결합하면 루트 레이아웃(app/layout.js)이 블로그 레이아웃(app/blog/layout.js)을 감싸고, 블로그(app/blog/page.js)와 블로그 게시물 페이지(app/blog/[slug]/page.js)를 순서대로 감싸게 됩니다.
동적 세그먼트 만들기
섹션 제목: “동적 세그먼트 만들기”동적 세그먼트를 사용하면 데이터로부터 생성되는 라우트를 만들 수 있습니다. 예를 들어 각 블로그 게시물마다 라우트를 수동으로 만드는 대신, 동적 세그먼트를 만들어 게시물 데이터에 기반해 라우트를 생성할 수 있습니다.
동적 세그먼트를 만들려면 세그먼트(폴더) 이름을 대괄호로 감싸세요: [segmentName]. 예를 들어 app/blog/[slug]/page.tsx 라우트에서 [slug]가 동적 세그먼트입니다.
app/blog/[slug]/page.tsx
JavaScriptTypeScript
export default async function BlogPostPage({ params, }: { params: Promise<{ slug: string }> }) { const { slug } = await params const post = await getPost(slug)
return ( <div> <h1>{post.title}</h1> <p>{post.content}</p> </div> ) }동적 세그먼트와 params props에 대해 더 알아보세요.
동적 세그먼트 안의 중첩 레이아웃 역시 params props에 접근할 수 있습니다.
검색 파라미터로 렌더링하기
섹션 제목: “검색 파라미터로 렌더링하기”서버 컴포넌트 페이지 에서 searchParams prop을 사용해 검색 파라미터에 접근할 수 있습니다:
app/page.tsx
JavaScriptTypeScript
export default async function Page({ searchParams, }: { searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) { const filters = (await searchParams).filters }searchParams를 사용하면 검색 파라미터를 읽기 위해 들어오는 요청이 필요하므로 페이지가 동적 렌더링에 참여하게 됩니다.
클라이언트 컴포넌트는 useSearchParams 훅을 사용해 검색 파라미터를 읽을 수 있습니다.
정적 렌더링 및 동적 렌더링 라우트에서의 useSearchParams 사용법을 알아보세요.
언제 무엇을 사용할까
섹션 제목: “언제 무엇을 사용할까”- 검색 파라미터로 페이지용 데이터를 로드 해야 할 때는
searchParamsprop을 사용하세요(예: 페이지네이션, 데이터베이스 필터링). - 검색 파라미터가 클라이언트에서만 사용될 때는
useSearchParams를 사용하세요(예: props로 이미 로드한 목록 필터링). - 작은 최적화로, 콜백이나 이벤트 핸들러 안에서는
new URLSearchParams(window.location.search)를 사용해 리렌더링 없이 검색 파라미터를 읽을 수 있습니다.
페이지 간 링크 연결하기
섹션 제목: “페이지 간 링크 연결하기”<Link> 컴포넌트를 사용해 라우트 간을 이동할 수 있습니다. <Link>는 HTML <a> 태그를 확장하여 프리패칭과 클라이언트 측 네비게이션을 제공하는 Next.js 내장 컴포넌트입니다.
예를 들어 블로그 게시물 목록을 생성하려면 next/link에서 <Link>를 가져오고 href prop을 전달합니다:
app/ui/post.tsx
JavaScriptTypeScript
import Link from 'next/link'
export default async function Post({ post }) { const posts = await getPosts()
return ( <ul> {posts.map((post) => ( <li key={post.slug}> <Link href={`/blog/${post.slug}`}>{post.title}</Link> </li> ))} </ul> ) }알아두면 좋아요 :
<Link>는 Next.js에서 라우트 간 이동의 기본 방식입니다. 보다 고급 네비게이션이 필요하면useRouter훅을 사용할 수 있습니다.
라우트 Props 헬퍼
섹션 제목: “라우트 Props 헬퍼”Next.js는 라우트 구조로부터 params와 명명된 슬롯을 추론하는 유틸리티 타입을 제공합니다:
- PageProps:
page컴포넌트용 props로params와searchParams를 포함합니다. - LayoutProps:
layout컴포넌트용 props로children과@analytics같은 명명된 슬롯을 포함합니다.
이 헬퍼는 전역에서 사용 가능하며 next dev, next build, 또는 next typegen을 실행할 때 생성됩니다.
app/blog/[slug]/page.tsx
export default async function Page(props: PageProps<'/blog/[slug]'>) { const { slug } = await props.params return <h1>Blog post: {slug}</h1> }app/dashboard/layout.tsx
export default function Layout(props: LayoutProps<'/dashboard'>) { return ( <section> {props.children} {/* If you have app/dashboard/@analytics, it appears as a typed slot: */} {/* {props.analytics} */} </section> ) }알아두면 좋아요
- 정적 라우트는
params가{}로 해석됩니다.PageProps,LayoutProps는 전역 헬퍼이므로 import가 필요 없습니다.- 타입은
next dev,next build,next typegen실행 중 생성됩니다.
API Reference
섹션 제목: “API Reference”API Reference를 읽어 이 페이지에서 언급된 기능을 더 알아보세요.
-
- 링크 및 네비게이션내장 네비게이션 최적화(프리패칭, 프리렌더링, 클라이언트 사이드 네비게이션 등) 작동 방식과 동적 라우트·느린 네트워크에 대한 최적화 방법을 알아보세요.
-
- layout.js 파일에 대한 API 레퍼런스입니다.
-
- page.js 파일에 대한 API 레퍼런스입니다.
-
- Link 컴포넌트내장
next/link컴포넌트로 빠른 클라이언트 사이드 네비게이션을 구현하세요.
- Link 컴포넌트내장
-
- 동적 데이터로부터 라우트 세그먼트를 프로그래밍 방식으로 생성할 수 있는 Dynamic Route Segments를 활용하세요.