NextJS 공식문서 파헤치기 (Defining Routes, Pages, Layouts and Templates)

Defining Routes

1️⃣ 경로 생성

  • 폴더를 사용하여 경로를 정의하는 파일 시스템 기반 라우터를 사용한다.
  • 각 폴더는 URL 세그먼트에 매핑되는 경로 세그먼트를 나타낸다.
  • page 파일이 없는 폴더는 공개적으로 액세스할 수 없다.

2️⃣ UI 만들기

NextJS 공식문서 파헤치기(1) 에서 알아보았던 파일 컨벤션에 따라 각 경로 세그먼트에 대하여 UI를 만들 수 있다.

// app/page.tsx
// 각 폴더에 page 파일을 사용하여 해당 폴더 라우트에 액세스할 수 있게 해준다.

export default function Page() {  
    return <h1>Hello, Next.js!</h1>
}

Pages

  • page.js는 경로에 고유한 UI 이다.
  • 컴포넌트를 내보내는 페이지를 정의할 수 있다.
  • ex) index 페이지를 생성하고 싶다면 app 폴더의 내에서 page.js를 생성하면 index.js 와 같은 역할을 한다.
// 아래의 예제에는 내보내는 컴포넌트 이름이 `Page`라고 되어있지만
// `npx create-next-app@latest`를 하여 초기 앱을 만들었을 때 '/' 경로의 page.tsx가 `Home`으로 되어있는 걸 보면
// 각 페이지의 경로 이름을 넣는것이 더 좋을것 같다.

// 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>
}

Layouts and Templates

1️⃣ 레이아웃

  • 여러 경로 간에 공유되는 UI
  • 레이아웃은 상태를 보존하고, 상호작용 하며, 다시 렌더링하지 않는다.
  • 레이아웃은 중첩 가능하다.
  • ex) /dashboard 에 레이아웃을 두면 /dashboard/settings 와 같은 하위 페이지들에서도 레이아웃이 공유된다.

2️⃣ 루트 레이아웃(필수)

  • app 디렉토리 최상위 수준에서 적용되며 모든 경로에서 적용된다.
  • 루트 레이아웃은 html, body 태그를 포함해야 하며 필수로 작성되어야한다. (루트 레이아웃만 hmtl,body 태그를 작성할 수 있음)
  • 서버에서 반환된 초기 HTML을 수정 가능
  • font, globalcss, meta 데이터 등등 의 정보가 들어갈 수 있다.
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        {/* Layout UI */}
        <main>{children}</main>
      </body>
    </html>
  )
}

3️⃣ 중첩 레이아웃

  • 폴더 계층의 레이아웃은 중첩 되어있다.
  • 특정 경로 세그먼트(폴더) 내부에 children을 추가하여 레이아웃을 중첩할 수 있다.
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section>{children}</section>
}

4️⃣ 템플릿

  • 자식 레이아웃이나 페이지를 래핑한다는 점에서 레이아웃과 유사하다.
  • 탐색 시 각 자식에 대해 새 인스턴스를 만든다. (경로 간에 지속되고 상태를 유지하는 레이아웃과의 차이점)
  • 따라서 템플릿을 공유하는 경로 사이를 탐색할 때 자식의 새 인스턴스가 마운트되고 DOM 요소가 다시 생성되고 클라이언트 컴포넌트의 상태가 보존되지 않는다. (리렌더링 된다)
  • 레이아웃보다 템플리이 더 적합할 때
    • 탐색을 다시 동기화
    • 탐색 시 자식 클라이언트 구성요소의 상태를 재설정
  • 템플릿은 template.js 파일에서 기본 리액트 컴포넌트를 내보내어 정의할 수 있다. (컴포넌트는 children prop을 허용해야 함)
// app/template.tsx
export default function Template({ children }: { children: React.ReactNode }) {
  return <div>{children}</div>
}

// 중첩의 관점에서 template.js는 레이아웃과 children 사이에 위치한다.
// 아래는 단순화된 출력 형식
<Layout>
  {/* Note that the template is given a unique key. */}
  <Template key={routeParam}>{children}</Template>
</Layout>

5️⃣ 예시

  • 메타데이터 API를 사용하여 HTML <head>,<title>,<meta> 요소 등을 수정할 수 있다.
  • 메타데이터는 layout.js, page.js 파일에서 metadata 객체나 generateMetadata 함수를 통해 정의할 수 있다.
// app/page.tsx or app/layout.tsx
import type { Metadata } from 'next'

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

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

✅ 알아두면 좋은 정보

  • 같은 폴더 내에 layout.js 와 page.js 가 함께 존재하게 된다면 layout이 page를 래핑한다.
  • 기본적으로 서버 컴포넌트이지만 클라이언트 컴포넌트로 설정 가능하다.
  • 부모 레이아웃과 자식 레이아웃간에 데이터 전달은 불가능하다. 하지만 한 경로에서 동일한 데이터를 두 번 이상 가져올 수 있으며, 리액트는 성능에 영향을 주지않고 자동적으로 중복된 요청을 제거한다.
  • pathname에 액세스할 수 없다. 하지만 가져온 클라이언트 컴포넌트는 usePathname hook을 사용하여 경로 이름에 액세스할 수 있다.
  • 레이아웃은 그 자체 아래의 경로 세그먼트에 액세스할 수 없다. 모든 경로 세그먼트에 액세스하려면 클라이언트 컴포넌트에서useSelectedLayoutSegment, useSelectedLayoutSegments를 사용해야한다.
  • Route Group을 사용하면 공유 레이아웃에서 특정 경로 구간을 선택하거나 제외할 수 있다.
  • Route Groups를 사용하여 여러 루트 레이아웃을 만들 수 있다.
  • 폴더 이름을 (folderName) 형식으로 만들면 Route Group을 사용할 수 있다.
  • 루트 레이아웃은 NextJS 12버전 page router의 _app.js, _document.js 파일을 대체할 수 있다.
  • 루트 레이아웃에 태그를 수동으로 추가하면 안된다. 대신 스트리밍 및 중복 제거 요소와 같은 고급 요구 사항을 자동으로 처리하는 Metadata API를 사용해야 한다.

📚 REFERENCE