ReactNextCentral

페이지와 레이아웃

Published on
앱 라우터를 이용하여 첫 번째 페이지와 공유 레이아웃을 생성하세요.
Table of Contents

Next.js 13의 앱 라우터는 페이지, 공유 레이아웃, 템플릿을 쉽게 생성할 수 있는 새로운 파일 규칙을 도입했습니다. 이 페이지는 Next.js 애플리케이션에서 이 특별한 파일들을 어떻게 사용하는지 안내해 드리겠습니다.


페이지

페이지는 라우트에 고유한 UI입니다. page.js 파일에서 컴포넌트를 내보내서 페이지를 정의할 수 있습니다. 중첩된 폴더를 사용하여 라우트를 정의하고 page.js 파일을 사용하여 라우트를 공개적으로 접근 가능하게 만들 수 있습니다.

app 디렉토리 내에 page.js 파일을 추가하여 첫 번째 페이지를 만들어보세요:

page.js 특별한 파일

app/page.tsx
// `app/page.tsx`는 `/` URL에 대한 UI입니다
export default function Page() {
  return <h1>안녕, 홈 페이지!</h1>
}
JavaScript
app/page.js
// `app/page.js`는 `/` URL에 대한 UI입니다
export default function Page() {
  return <h1>안녕, 홈 페이지!</h1>
}

app/dashboard/page.tsx
// `app/dashboard/page.tsx`는 `/dashboard` URL에 대한 UI입니다
export default function Page() {
  return <h1>안녕, 대시보드 페이지!</h1>
}
JavaScript
app/dashboard/page.js
// `app/dashboard/page.js`는 `/dashboard` URL에 대한 UI입니다
export default function Page() {
  return <h1>안녕, 대시보드 페이지!</h1>
}

레이아웃

레이아웃은 여러 페이지 간에 공유되는 UI입니다. 내비게이션시 레이아웃은 상태를 유지하고 상호 작용이 가능하며 다시 렌더링되지 않습니다. 레이아웃은 중첩될 수도 있습니다.

layout.js 파일에서 React 컴포넌트를 default로 내보내어 레이아웃을 정의할 수 있습니다. 이 컴포넌트는 렌더링 중에 하위 레이아웃(있는 경우) 또는 하위 페이지로 채워질 children prop을 수용해야 합니다.

layout.js 특별한 파일

app/dashboard/layout.tsx
export default function DashboardLayout({
  children, // 페이지 또는 중첩된 레이아웃이 될 것입니다
}: {
  children: React.ReactNode
}) {
  return (
    <section>
      {/* 여기에 공유 UI (예: 헤더 또는 사이드바)를 포함하세요 */}
      <nav></nav>

      {children}
    </section>
  )
}
JavaScript
app/dashboard/layout.js
export default function DashboardLayout({
  children, // 페이지 또는 중첩된 레이아웃이 될 것입니다
}) {
  return (
    <section>
      {/* 여기에 공유 UI (예: 헤더 또는 사이드바)를 포함하세요 */}
      <nav></nav>

      {children}
    </section>
  )
}

Root 레이아웃

Root 레이아웃은 app 디렉터리의 최상위에서 정의되며 모든 라우트에 적용됩니다. 이 레이아웃을 사용하면 서버에서 반환되는 초기 HTML을 수정할 수 있습니다.

app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
JavaScript
app/layout.js
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
  • app 디렉터리에는 루트 레이아웃이 반드시 포함되어야 합니다.
  • 루트 레이아웃은 Next.js가 자동으로 생성하지 않기 때문에 <html><body> 태그를 정의해야 합니다.
  • <head> HTML 요소를 관리하기 위해, 예를 들면 <title> 요소 등, 내장 SEO 지원을 사용할 수 있습니다.
  • 라우트 그룹을 사용하여 여러 루트 레이아웃을 만들 수 있습니다. 여기에서 예시를 확인하세요.
  • 루트 레이아웃은 기본적으로 서버 컴포넌트이며 클라이언트 컴포넌트로 설정될 수 없습니다.
  • pages 디렉터리에서 이동: 루트 레이아웃은 _app.js_document.js 파일을 대체합니다. 마이그레이션 가이드를 확인하세요.

레이아웃 중첩하기

폴더 내에 정의된 레이아웃(예: app/dashboard/layout.js)은 특정 라우트 세그먼트(예: acme.com/dashboard)에 적용되며 해당 세그먼트가 활성화될 때 렌더링됩니다. 기본적으로 파일 계층 구조의 레이아웃은 중첩되어 있으며, 그것은 children prop을 통해 자식 레이아웃을 감싼다는 것을 의미합니다.

중첩된 레이아웃

app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section>{children}</section>
}
JavaScript
app/dashboard/layout.js
export default function DashboardLayout({ children }) {
  return <section>{children}</section>
}
  • <html><body> 태그는 오직 루트 레이아웃에만 포함될 수 있습니다.

위의 두 레이아웃을 결합하면, 루트 레이아웃(app/layout.js)은 대시보드 레이아웃(app/dashboard/layout.js)을 감싸게 되며 대시보드 레이아웃은 app/dashboard/* 내의 라우트 세그먼트를 감싸게 됩니다.

두 레이아웃은 다음과 같이 중첩됩니다.

중첩된 레이아웃들

Route Groups를 사용하여 특정 라우트 세그먼트를 공유 레이아웃에 포함시키거나 배제할 수 있습니다.


템플릿

템플릿은 각 자식 레이아웃이나 페이지를 감싸는 레이아웃과 유사합니다. 라우트 간에 지속되고 상태를 유지하는 레이아웃과 달리 템플릿은 네비게이션마다 각각의 자식에 대해 새로운 인스턴스를 생성합니다. 이는 사용자가 같은 템플릿을 공유하는 라우트 사이를 이동할 때 컴포넌트의 새 인스턴스가 마운트되고 DOM 요소가 재생성되며 상태는 보존되지 않으며, 효과가 재동기화된다는 것을 의미합니다.

특정한 동작이 필요한 경우가 있을 수 있으며 이런 경우 템플릿이 레이아웃보다 더 적합한 옵션이 될 수 있습니다. 예를 들면,

  • useEffect (예: 페이지 뷰 기록) 및 useState (예: 페이지별 피드백 양식)에 의존하는 기능.
  • 기본 프레임워크 동작을 변경하기 위해. 예로, 레이아웃 내의 Suspense 경계는 페이지 전환시가 아닌 레이아웃이 처음 로드될 때만 fallback을 표시합니다. 그러나 템플릿의 경우, 각 네비게이션마다 fallback이 표시됩니다.

템플릿은 template.js 파일에서 기본 React 컴포넌트를 내보내는 것으로 정의될 수 있습니다. 컴포넌트는 children prop을 받아야 합니다.

template.js 특별한 파일

app/template.tsx
export default function Template({ children
}: { 
    children: React.ReactNode 
}) {
  return <div>{children}</div>
}
JavaScript
app/template.js
export default function Template({ children }) {
  return <div>{children}</div>
}

중첩 측면에서 template.js는 레이아웃과 그 자식 사이에서 렌더링됩니다. 여기에 간략한 출력이 있습니다.

<Layout>
  {/* 템플릿에는 고유한 키가 주어집니다. */}
  <Template key={routeParam}>{children}</Template>
</Layout>

<head> 수정하기

app 디렉터리에서는 내장 SEO 지원을 사용하여 titlemeta와 같은 <head> HTML 요소를 수정할 수 있습니다.

메타데이터는 layout.js 또는 page.js 파일에서 metadata 객체generateMetadata 함수를 내보내는 것으로 정의될 수 있습니다.

app/page.tsx
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Next.js',
}

export default function Page() {
  return '...'
}
JavaScript
app/page.js
export const metadata = {
  title: 'Next.js',
}

export default function Page() {
  return '...'
}

루트 레이아웃에 수동으로 <head> 태그를 추가해서는 안 됩니다. 대신 Metadata API를 사용해야 하며 이 API는 스트리밍 및 <head> 요소 중복 제거와 같은 고급 요구사항을 자동으로 처리합니다.

API 참조에서 사용 가능한 메타데이터 옵션에 대해 자세히 알아보세요.