[번역] Next.js13 App Router - Routing - Pages and Layouts

최영호·2023년 7월 27일
1

On this page

Routing FundamentalsDefining Routes 페이지를 먼저 읽고 오길 강력하게 추천드립니다.

Next.js13 버전에 출시 된 App Router는 페이지, 공유 레이아웃, 템플릿을 쉽게 생성할 수 있는 새로운 파일 컨벤션을 도입하였습니다.
해당 문서는 Next.js 어플리케이션에서 어떻게 이런 특수 파일들을 사용할 수 있는지 안내합니다.

Pages

하나의 페이지는 특정 라우트의 UI를 담당합니다.
page.js 파일에서 컴포넌트를 export 하면서 페이지를 정의할 수 있습니다.
중첩 폴더를 사용하여 라우트를 정의하고 라우트마다 page.js 파일을 생성하여 라우트가 공개적으로 접근 가능하도록 만드세요.

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

// `app/page.tsx` 는 `/` URL을 위한 UI입니다.
export default function Page() {
  return <h1>Hello, Home page!</h1>
}
// `app/dashboard/page.tsx` 는 `/dashboard` URL을 위한 UI 입니다.
export default function Page() {
  return <h1>Hello, Dashboard Page!</h1>
}

참고 사항:

  • 페이지는 항상 라우트 서브 트리리프 노드입니다.
  • .js, .jsx, .tsx 파일 확장자 모두 페이지 파일로 사용할 수 있습니다.
  • page.js 파일은 라우트 세그먼트를 공개적으로 접근할 수 있도록 하는데 필수적인 요소입니다.
  • 페이지는 서버 컴포넌트가 기본이지만 클라이언트 컴포넌트로 설정할 수 있습니다.
  • 페이지는 데이터를 패치할 수 있습니다. Data Fetching 섹션을 확인해 보세요.

Layouts

레이아웃은 다수의 페이지 간에 공유 될 UI 입니다.
네비게이션에서 레이아웃은 스테이트와 인터랙티브함을 유지하고 리렌더링 되지 않습니다.
레이아웃은 중첩 될 수 있습니다.

레이아웃을 정의하기 위해선 layout.js 파일에서 리액트 컴포넌트를 default export 하면 됩니다.
해당 컴포넌트는 children 프롭을 받을 수 있어야 하며 children 프롭으론 자식 레이아웃 컴포넌트나 자식 페이지 컴포넌트가 위치할 수 있습니다.

export default function DashboardLayout({
  children, // 페이지 혹은 중첩 레이아웃이 될 수 있습니다. 
}: {
  children: React.ReactNode
}) {
  return (
    <section>
      {/* 헤더나 사이드바 같은 공유 UI를 여기에 추가하세요 */}
      <nav></nav>
 
      {children}
    </section>
  )
}

참고 사항:

  • 어플리케이션의 최상단 레이아웃은 루트 레이아웃이라고 불립니다. 이 루트 레이아웃은 필수적인 요소이며 어플리케이션에 있는 모든 페이지에 적용 됩니다. 루트 레이아웃은 반드시 html, body 태그를 포함해야 합니다.
  • 어떤 라우트 세그먼트도 자신만의 레이아웃을 정의할 수 있습니다. 이들 레이아웃은 해당 세그먼트에 있는 모든 페이지에 공유됩니다.
  • 라우트에 있는 레이아웃은 기본적으로 중첩 됩니다. 부모 레이아웃은 리액트의 children 프롭을 통해 자식 레이아웃을 래핑합니다.
  • 라우트 그룹을 사용하여 특정 라우트 세그먼트와 공유 레이아웃을 선택할 수 있습니다.
  • 레이아웃은 기본적으로 서버 컴포넌트이며 클라이언트 컴포넌트로 설정할 수도 있습니다.
  • 레이아웃은 데이터 패치를 할 수 있습니다. Data Fetching 페이지에서 더 자세한 내용을 확인해 보세요.
  • 부모 레이아웃과 children 프롭으로 전달한 컴포넌트 간에 데이터를 전달하는건 불가능합니다. 그러나 children 프롭으로 전달 된 컴포넌트에서 동일 데이터를 한번 이상 패치할 수 있고 리액트는 퍼포먼스 저하 없이 자동으로 중복 된 리퀘스트를 정리해줍니다.
  • 레이아웃은 현재 라우트 세그먼트에 접근할 수 없습니다. 라우트 세그먼트에 접근하기 위해선 클라이언트 컴포넌트에서 useSelectedLayoutSegment 또는 useSelectedLayoutSegments 훅을 사용할 수 있습니다.
  • .js, .jsx, .tsx 파일 확장자 모두 레이아웃 파일로 사용할 수 있습니다.
  • layout.js 파일과 page.js 파일은 동일 폴더내에서 정의 될 수 있습니다. 레이아웃은 페이지를 래핑하게 될것입니다.

Root Layout (Required)

루트 레이아웃은 app 디렉토리의 최상단에서 정의 되고 모든 라우트에 적용 됩니다.
루트 레이아웃은 서버에서 리턴 되는 초기 HTML을 수정할 수 있도록 도와줍니다.

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

참고 사항:

  • app 디렉토리는 루트 레이아웃을 반드시 가지고 있어야 합니다.
  • 루트 레이아웃은 반드시 <html> <body> 태그를 정의해야 하는데, 그 이유는 Next.js에서 이들을 자동으로 생성해주지 않기 때문입니다.
  • 빌트인 SEO 지원 기능을 통해 <head> 태그를 관리할 수 있습니다. 예를 들어 <title> 태그를 관리할 수 있습니다.
  • 라우트 그룹을 사용하여 다수의 루트 레이아웃을 생성할 수 있습니다. 다음의 예시를 확인해 보세요.
  • 루트 레이아웃은 기본적으로 서버 컴포넌트이며 클라이언트 컴포넌트 설정할 수 없습니다.

pages 디렉토리에서 마이그레이션 가이드라인:
루트 레이아웃은 _app.js, _document.js 파일을 대체합니다. 마이그레이션 가이드 문서를 확인해 보세요.

Nesting Layouts

app/dashboard/layout.js 와 같이 폴더 내에 정의 된 레이아웃은 acme.com/dashboard 같은 특정한 라우트 세그먼트에 적용 되고 해당 세그먼트들이 활성화 상태일때 렌더링 됩니다.
기본적으로 파일 계층 구조에 놓여있는 레이아웃은 중첩 됩니다. 이는 레이아웃이 children 프롭을 활용하여 자식 레이아웃을 래핑한다는 의미입니다.

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section>{children}</section>
}

참고 사항:

  • 루트 레이아웃만 <html>, <body> 태그를 가질 수 있습니다.

두개 이상의 레이아웃을 결합 하는 경우 루트 레이아웃(app/layout.js)은 app/dashboard/* 내에 위치한 라우트 세그먼트를 래핑하는 대시보드 레이아웃(app/dashboard/layout.js)을 래핑합니다.

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

라우트 그룹을 사용하여 특정 라우트 세그먼트와 공유 레이아웃을 선택할 수 있습니다.

Templates

템플릿은 자식 레이아웃과 페이지 컴포넌트를 래핑한다는 점에선 레이아웃과 비슷합니다.
라우트 간에 스테이트를 유지하는 레이아웃과는 다르게 템플릿은 네비게이션마다 매번 새로운 자식 컴포넌트를 생성합니다.
이는 유저가 템플릿을 공유하는 라우트 간에 네비게이션하는 동안 새로운 컴포넌트가 매번 마운트 되고 DOM 요소 역시 새롭게 만들어지고 스테이트는 보존 되지 않고 이펙트는 다시 조율 됩니다.

이런 특수한 기능이 필요한 순간이 있을 수 있고 템플릿은 레이아웃보다 더 적합한 선택일 수 있습니다.
다음은 관련 예시입니다

  • CSS 혹은 애니메이션 라이브러리를 사용하여 입장/퇴장 애니메이션을 사용하는 경우
  • useEffect (예를 들어 페이지 뷰를 로깅하는 경우) , useState (예를 들어 페이지당 피드백 폼이 필요한 경우)에 의존하는 기능이 있는 경우
  • 기존 프레임워크 기능을 변경하는 경우
    • 예를 들어 레이아웃 내에 있는 Suspense Boundary는 레이아웃이 초기 로딩 되었을 때만 fallback 요소를 보여주고 페이지 전환에선 보여주지 않습니다. 반대로 템플릿의 경우 매 네비게이션마다 fallback이 등장하게 됩니다.

추천 사항:
템플릿을 사용해야 하는 특수한 이유가 없다면 레이아웃을 사용하길 권장합니다.

템플릿은 template.js 파일에서 리액트 컴포넌트를 export 하여 정의 할 수 있습니다.
중첩 되는 세그먼트를 표현하기 위해 children 프롭을 가져야 합니다.

export default function Template({ children }: { children: React.ReactNode }) {
  return <div>{children}</div>
}

레이아웃과 템플릿을 같이 사용한 라우트 세그먼트의 렌더링 결과물은 다음과 같습니다.

<Layout>
  {/* 템플릿 컴포넌트에 유니크한 키값이 적용 된걸 주목하세요. */}
  <Template key={routeParam}>{children}</Template>
</Layout>

Modifying <head>

app 디렉토리에서 빌트인 SEO 지원 기능을 통해 title 이나 meta 같은 <head> 태그 요소들을 수정할 수 있습니다.

메타데이터는 metadata 객체를 export 하거나 layout.js, page.js 에서 generateMetadata 함수를 통해 정의할 수 있습니다.

import { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'Next.js',
}
 
export default function Page() {
  return '...'
}

참고 사항:
루트 레이아웃에서 수동으로 <title>, <meta> 같은 <head> 태그 요소를 추가해선 안됩니다.
대신 Metadata API 를 사용하여 <head> 태그의 스트리밍이나 중복 제거 같은 고급 요구사항들을 자동으로 처리할 수 있도록 해야 합니다.

API 레퍼런스에서 사용 가능한 메타데이터 옵션들을 알아보세요.

profile
무친 프론트엔드 개발자를 꿈꾸며...

0개의 댓글