Next.js App Router에 대해 알아보자(3. 페이지와 레이아웃)

한상욱·2023년 8월 27일
0

Next.js 13 기초

목록 보기
3/6

페이지

경로에 대한 고유 UI입니다. page.tsx에서 만듭니다.

// app/blog/dashboard/page.tsx -> URL 'blog/dashboard'에 UI를 제공해줍니다.
const Page = () => <div>블로그</div>

export default Page

// app/page.tsx -> / URL '/'에 UI를 제공해줍니다.
const Page = () => <div></div>

페이지에 대해 다음 사실을 알면 도움이 됩니다.

  1. 페이지는 모든 라우터 서브트리에 리프노드입니다.
  2. 페이지는 기본적으로 서버 컴포넌트로 구성되지만 클라이언트 컴포넌트로도 사용이 가능합니다.
  3. 페이지는 데이터 패치도 가능합니다. 이 부분은 뒤에서 더 자세히 다룹니다.

레이아웃

레이아웃은 여러 페이지에서 공유하는 UI입니다. 탐색하는 시점에도 레이아웃은 상태를 보존하고 인터랙티브하며 리랜더링이 일어나지 않습니다. 레이아웃 또한 중첩이 가능합니다.

레이아웃은 layout.tsx로 정의가 가능합니다. children을 props로 받을 수 있고 렌더링하는 동안 하위 레이아웃이나 하위 페이지로 채워집니다.

const DashboardLayout = ({
    children, // will be a page or nested layout
  }: {
    children: React.ReactNode
  }) => {
    return (
      <section>
        <nav>
          <ul style={{display: 'flex'}}>
            <li style={{marginRight: '5px'}}>대쉬보드</li>
            <li style={{marginRight: '5px'}}>친구찾기</li>
            <li>설정</li>
          </ul>
        </nav>   
        {children}
      </section>
    )
  }

export default DashboardLayout

페이지는 알아서 children에 들어갑니다.

레이아웃에 대해 아래 사실을 알면 도움이 됩니다.

  1. 모든 라우터 세그먼트는 옵셔널하게 layout을 정의할 수 있습니다.
  2. 라우터 그룹을 이용해 세그먼트 내부와 외부 모두 레이아웃을 공유할 수 있게 만들 수 있습니다.
  3. 레이아웃도 데이터 패칭이 가능합니다.
  4. 레이아웃도 디폴트는 서버 컴포넌트이고 클라이언트 컴포넌트로도 사용이 가능합니다.
  5. 부모 레이아웃과 자식 레이아웃 간의 데이터 전달은 불가능합니다. 하지만 경로에서 동일한 데이터를 두 번 이상 가져올 수 있고 React 성능에 영향을 주지 않고 자동으로 중복 요청은 제거합니다.
  6. 레이아웃은 현재 라우터 세그먼트에 접근할 수 없습니다. 라우터 세그먼트에 접속하기 위해서는 클라이언트 컴포넌트에서만 사용 가능한 함수(useSelectedLayoutSegment, useSelectedLayoutSegments)들을 사용해야 합니다.

루트 레이아웃

루트 레이아웃은 앱 디렉토리 최상단에 정의하고 모든 라우터에 적용이 가능합니다. 이 레이아웃은 서버에서 반환된 내부 HTML을 수정해서 반환할 수 있습니다.

import './globals.css'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

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

루트 레이아웃에 대해 아래 사실을 알면 도움이 됩니다.

  1. app 디렉토리는 반드시 루트 레이아웃을 포함해야 합니다.
  2. 루트 레이아웃은 반드시 html과 body 태그를 포함해야 합니다.
  3. head 태그에 SEO 지원과 관련한 여러 정보를 넣을 수 있습니다.
  4. 라우터 그룹을 사용하면 다수의 루트 레이아웃을 만들 수 있습니다. 추후 다룹니다.
  5. 루트 레이아웃도 기본적으로 서버 컴포넌트이지만 클라이언트 컴포넌트로도 사용할 수 있습니다.

중첩 레이아웃

기본적으로 레이아웃은 파일 계층으로 중첩이 가능하고 children으로 들어갑니다.

// app/blog/layout.tsx
const DashboardLayout = ({
	children, // will be a page or nested layout
  }: {
	children: React.ReactNode
  }) => {
	return (
	  <section>
			<div style={{background: 'orange', width: '500px', height: '700px'}}>
				{children}
			</div>
	  </section>
	)
  }

export default DashboardLayout

// app/blog/dashboard/layout.tsx
const DashboardLayout = ({
    children, // will be a page or nested layout
  }: {
    children: React.ReactNode
  }) => {
    return (
      <section>
        <nav>
          <ul style={{display: 'flex'}}>
            <li style={{marginRight: '5px'}}>대쉬보드</li>
            <li style={{marginRight: '5px'}}>친구찾기</li>
            <li>설정</li>
          </ul>
        </nav>   
        {children}
      </section>
    )
  }

export default DashboardLayout

이렇게 부모 layout의 children으로 자식 layout이 들어가는 것을 알 수 있습니다.

템플릿

템플릿도 레이아웃과 비슷하게 각각의 자식 레이아웃이나 페이지를 감싸는 용도로 사용됩니다. 템플릿은 레이아웃과 다르게 탐색할 때마다 자식 요소마다 새로운 인스턴스를 생성합니다. 이는 템플릿을 공유하는 라우터 간에 이동할 대 컴포넌트의 새로운 인스턴스가 마운트 되고 DOM 엘리먼트가 새로 생기고 상태는 보존되지 않고 효과가 재동기화 된다는 것을 말합니다.

템플릿을 레이아웃 대신 써야하는 조건은 다음과 같습니다.

  1. useEffect와 useState에 종속된 경우
  2. 프레임워크의 기본 동작을 바꿔야 하는 경우(ex. 각 페이지마다 탐색 시, 대체 콘텐츠를 다르게 표현하고 싶을 때)

권장 사항은 템플릿을 사용하지 않는 것입니다.

템플릿도 app 디렉토리 어딘가에 template.tsx로 정의하면 사용할 수 있습니다.

const Template = ({
    children, 
  }: {
    children: React.ReactNode
  }) => {
    return (
      <div>
        {children}
      </div>
    )
  }

export default Template

레이아웃과 템플릿이 공존할 때 결과물은 다음과 같습니다.

<Layout>
  {/* Note that the template is given a unique key. */}
  <Template key={routeParam}>{children}</Template>
</Layout>

헤드 수정하기

SEO를 담당하는 부분입니다. 타이틀이나 메타를 사용합니다.

메타데이터는 metadata 객체나 generateMetaData 함수를 이용해 layout이나 page에 넣어줍니다.

import { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: '갱이❤️우기',
}

// Page에 title 갱이❤️우기가 심긴다.
export default function Page() {
  return '...'
}

헤드 수정하기에서 알면 좋은 것들은 다음과 같습니다.

루트 레이아웃에 head나 title, meta를 직접 추가하면 안됩니다. 대신 MetadataAPI를 사용하여 스트리밍 및 head 요소 중복 제거와 같은 고급 요구 사항을 자동으로 처리합시다.

profile
그냥 뛰는 사람

0개의 댓글