본문 바로가기
개발(Development)/Next.js 13

[Next.js 13] layout.js: 레이아웃 기본 정리

by 카레유 2023. 8. 29.

# Next.js 13 - 레이아웃(layout.js)란?

Next.js 13부터 도입된 layout.js 파일은 여러 페이지(page.js) 들에 공통적으로 적용되는 UI를 정의하는 컴포넌트이다.

layout.js는 하위의 page.js 및 layout.js 를 자식(children)으로 감싸서 화면을 렌더링 한다.

따라서 여러 레이아웃을 만들어 두고, 부모-자식 레이아웃 구조로 중첩하여 적용하는 방식도 가능하다.

 

또한 레이아웃(Layout)에 정의된 UI와 상태 값들은, (경로 이동-Navigation 등이 발생하더라도) 계속해서 유지되며 재렌더링 되지 않고 재활용된다.


# layout.js(레이아웃) 정의 방법

layout.js 를 정의하는 방법은 다음과 같다.

 

  1. /app 디렉터리, 혹은 그 내부의 중첩된 폴더에 layout.js 파일을 생성한다.
  2. layout.js 파일 내부에 React 컴포넌트 함수를 정의하고, Prop으로 children을 받도록 한다.
  3. 정의한 컴포넌트 함수를 `export default` 처리 한다.

 

예를 들어, `/app/dashboard/layout.js` 파일은 아래와 같이 정의할 수 있다.

 

export default function DashboardLayout({
  children, // will be a page or nested layout
}: {
  children: React.ReactNode;
}) {
  return (
    <section>
      {/* Include shared UI here e.g. a header or sidebar */}
      <nav></nav>

      {children}
    </section>
  );
}

 

이렇게 정의한 layout.js 컴포넌트는 children 프롭을 통해 page.js 컴포넌트나 자식 layout.js 를 내부에 렌더링 한다.

 


# 루트 레이아웃(Root Layout)

루트 레이아웃은 /app 디렉토리에 정의된 최상위 layout.js 파일로, 모든 라우트 경로에 적용된다.

즉, /app 경로 및 하위의 중첩 폴더에 정의된 모든 page.js 및 layout.js 컴포넌트들은 루트레이아웃의 자식(children)이 되어 렌더링된다.

 

루트 레이아웃은 가장 상위의 껍데기(?)로서 html 과 body 태그를 포함하며,

개발자가 정의한 다양한 리액트 컴포넌트 들을 자식(children)으로 렌더링 한다.

 

/app/layout.tsx 파일(루트 레이아웃):

 

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

 

단, 루트 레이아웃은 다음 사항을 반드시 준수해야 한다.

 

  • /app 디렉토리는 반드시 루트 레이아웃(layout.js)을 포함해야 한다.
    • 다른 중첩 폴더에 layout.js가 있더라도, 반드시 /app 최상위 레벨에 루트 layout.js 파일이 존재해야 한다.
    • 필요에 따라 (Route Group)으로 구분하여 정의할 수도 있다.
  • `<html>`과 `<body>` 태그를 포함해야 한다.
    • 오직 루트 레이아웃만 이 태그들을 포함할 수 있다.
    • 다른 하위의 레이아웃은 이 태그들을 포함할 수 없다.
  • 반드시 Server Component여야 한다.
    • 루트 레이아웃은 Client Component로 전환해선 안 된다.
    • 루트 레이아웃을 제외한 하위의 다른 레이아웃들은 Client Component로 전환할 수 있다.

# 중첩 레이아웃(Nesting Layout)

각각의 접속 경로 폴더 (Route Segment)마다 별도의 layout.js 파일을 정의할 수 있다.

 

이렇게 정의한 layout.js는 해당 경로(폴더)에 정의된 page.js를 감싸는 레이아웃이 되며,

해당 경로(Route Segment)로 접속할 때만 적용된다.

 

예를 들어, `app/dashboard/layout.tsx` 레이아웃은 `http://domain.com/dashboard` 경로로 접속할 때만 적용된다.

 

레이아웃은 상위에서 하위로 계속하여 중첩 적용된다.

즉, 상위 레이아웃은 chidren 프롭을 통해 하위 레이아웃을 내부에 렌더링하는 한다. 

 

예를 들어, app/dashboard/layout.tsx 파일을 정의하면,

 

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

 

dashboard 레이아웃 (app/dashboard/layout.tsx)는 상위의 루트 레이아웃(app/layout.tsx)에 의해 감싸지며,

결과적으로 아래와 같은 구조로 렌더링 된다.

 


# layout.js 컴포넌트의 파라미터

layout.js 컴포넌트는 2개의 파라미터를 Props으로 받는다.

 

  • children (required)
    • 레이아웃이 감싸서 렌더링하는 자식 컴포넌트(React.ReactNode 타입)가 들어온다.
    • 대표적으로 "중첩 layout.js", "자식 page.js", "loading.js", "error.js" 등이 들어올 수 있다.
  • params (optional)
    • page.js와 동일하게, 동적 라우팅(Daynamic Route)의 접속 경로가 들어온다.
    • 단, 루트 레이아웃(Root Layout)은 params 프롭을 받지 않는다. 오직 중첩 레이아웃들만 이 프랍을 받는다.

 

참고로, layout.js 는 page.js와 다르게 쿼리 스트링(searchParam)을 파라미터로 받지 않는다.

레이아웃은 재렌더링(Re-rendering)되지 않기 때문에, 경로 이동에 따라 쿼리 스트링이 바뀌어도 새로 갱신되지 않기 때문이다.

따라서 page.js 에서 `searchParams` 프랍을 사용하거나, Client Component에서 `useSearchParams` 훅을 사용해야 한다.


# layout.js 요약 정리

 

  • 루트 레이아웃(Root Layout)
    • 루트 레이아웃(`/app/layout.js)은 반드시 존재해야 한다.
    • 루트 레이아웃은 Nextjs 웹 애플리케이션의 모든 page.js에 적용된다.
    • 루트 레이아웃은 반드시 `<html>`, `<body>` 태그를 포함해야 한다.
    • 루트 레이아웃은 반드시 Server Component여야 하며, Client Component로 전환 불가하다.

 

  • 중첩 레이아웃(Nesting Layout)
    • 모든 라우트 경로(route segment)에 별도의 레이아웃(layout.js)를 정의할 수 있으며,  해당 경로의 page.js에만 적용된다.
    • 레이아웃은 기본적으로 중첩되어 적용된다. 따라서 상위 경로의 레이아웃은 하위 경로의 레이아웃을 감싼다.

 

  • 서버/클라이언트 컴포넌트(Server/Client Component)
    • Layout은 디폴트로 서버 컴포넌트이다.
      • 따라서, asycn 컴포넌트 함수로 정의하여, 직접 await fetch() 호출이 가능하다.
      • 단, 부모-자식 레이아웃 간에는 데이터 전달이 불가하다.
      • 하지만, 부모 레이아웃, 자식 레이아웃에서 동일한 fetch() 각각 호출해도 성능에 전혀 영향이 없다.
      • Request Memoization 를 통해 동일한 경로의 fetch() 호출이 있으면 자동으로 중복 제거(Dedupe)처리 해주기 때문이다.
    • Layout은 파일 최상단에 "use client"를 표기하여 Client Component로 전환할 수 있다.
      • 단 루트 레이아웃(Root Layout)은 반드시 Server Component 여야만 한다.

 

  • SEO(검색 최적화) 지원
    • HTML `<head>` 태그의 title, description 도 개발자가 컨트롤할 수 있다.
    • 단, 직접 루트 레이아웃에서 head 태그를 추가하고,title, meta 태그를 정의하지 말자.
    • Next.js에서 제공하는 MetaData API를 이용하면 된다. layout.js 및 page.js 파일에 서 사용 가능하다.
      • metadata 객체 : 변하지 않는 정적 메타 데이터(Static meta data)를 정의할 때 사용한다.
      • generateMetadata() 함수: 서버 등에서 매번 불러와야 하는 하는 동적 메타 데이터(Dynamic meta data)를 정의할 때 사용한다.

 

  • 라우트 그룹(Route Groups)
    • /app 내부에서 폴더명을 (그룹명)으로 정의하면, 라우트 그룹화가 가능하다.
    • 라우트 그룹마다 별도의 레이아웃을 적용할 수도 있다.

 

  • 접속 경로(Route Segment)
    • /app 내부에서 폴더명을 /[id] 나 /[category] 등으로 정의하면, 해당 위치의 경로는 다양한 주소로 접속 가능한 동적 라우트(Dynaimc Route)가 된다.
    • 실제로 브라우저에서 접속한 경로명 (id나 category 등)은 param 프롭으로 받을 수 있다.
    • 단, 루트 레이아웃이 아닌 중첩 레이아웃에서만 받을 수 있다.
    • 루트 레이아웃은 param 프랍을 통해서는 현재 접속 경로(route segment)를 파악할 수 없으므로, Client Component의 useSelectedLayoutSegment나 useSelectedLayoutSegments 훅을 이용해야 한다.

 

  • Search Params(쿼리 스트링)
    • Layout은 page.js와 다르게 쿼리 스트링을 searchParams 프랍으로 받지 못한다.
    • page.js의 searchParams 프랍이나, Client Component의 `useSearchParms` 훅을 이용해야 한다.

이 글은 아래의 Next.js 공식 문서 내용을 참고하여 정리한 글이다.

Pages and Layout Docs

- layout.js API Reference

 

참고로 Next.js 13은 나온지 얼마 안 되서 그런지,

공식문서의 내용이 수시로 바뀌고, 사라졌다 생겼다를 반복하고 있다.(2023.8 기준)

 

중요한 부분은 직접 문서를 확인하고 적용할 필요성이 있을듯 하다.

 

 

댓글