티스토리 뷰

개발공부/⬛ Next.js

[Next.js] Global Layout과 페이지별 레이아웃 적용하기 (Using Pages Router)

2024. 9. 10. 18:43

최상위 컴포넌트인 App 컴포넌트를 먼저 살펴보자.

// _app.tsx

import "@/styles/globals.css";
import type { AppProps } from "next/app";

export default function App({ Component, pageProps }: AppProps) {
  return (
      <Component {...pageProps} />
  );
}

 

App 컴포넌트가 전달받는 props 중 Component라는 props는 현재 접속하는 페이지 컴포넌트이다.

 

 

따라서 모든 페이지의 적용될 레이아웃이 있다면 App 컴포넌트와 같이 최상위 컴포넌트에 적용해주면 된다.

// global-layout.tsx : 모든 페이지에 적용할 레이아웃

import Link from "next/link";
import { ReactNode } from "react";
import style from "./global-layout.module.css";

export default function GlobalLayout({ children }: { children: ReactNode }) {
  return (
    <div className={style.container}>
      <header className={style.header}>
        <Link href={"/"}>📚 ONEBITE BOOKS</Link>
      </header>
      <main className={style.main}>{children}</main>
      <footer className={style.footer}>제작 @2024</footer>
    </div>
  );
}
// _app.tsx : 최상위 컴포넌트

import GlobalLayout from "@/components/global-layout";
import "@/styles/globals.css";
import type { AppProps } from "next/app";

export default function App({ Component, pageProps }: AppProps) {
  return (
    <GlobalLayout>
      <Component {...pageProps} />
    </GlobalLayout>
  );
}

 

 

 

 

특정 페이지에만 적용할 레이아웃은 레이아웃을 적용할 페이지의 컴포넌트에 getLayout이라는 속성을 추가해서 적용할 수 있다.

App 컴포넌트에서 페이지 컴포넌트를 바로 리턴하지 않고 페이지 컴포넌트의 getLayout 메서드의 인수로

페이지 컴포넌트를 넣어주면 특정 페이지에 원하는 레이아웃을 적용할 수 있다.

// searchable-layout.tsx : Home 페이지에만 적용할 레이아웃

import { ReactNode } from "react";

export default function SearchableLayout({ children }: { children: ReactNode }) {
  return (
    <div>
      <div>임시 서치바</div>
      {children}
    </div>
  );
}
// index.tsx : 레이아웃을 적용할 페이지

import SearchableLayout from "@/components/searchable-layout";
import style from "./index.module.css";
import { ReactNode } from "react";

export default function Home() {
  return (
    <>
      <h1 className={style.h1}>인덱스</h1>
    </>
  );
}

Home.getLayout = (page: ReactNode) => {
  return <SearchableLayout>{page}</SearchableLayout>;
};
// _app.tsx : 최상위 컴포넌트

import GlobalLayout from "@/components/global-layout";
import "@/styles/globals.css";
import type { AppProps } from "next/app";

export default function App({ Component, pageProps }: AppProps) {
  const getLayout = Component.getLayout;

  return (
    <GlobalLayout>
      {getLayout(<Component {...pageProps} />)}
    </GlobalLayout>
  );
}

 

 

위와 같이 작성하면 getLayout를 설정하지 않은 페이지로 접근하면 getLayout값이 undefined이기 때문에

오류가 발생한다.

 

// _app.tsx

// ...
const getLayout = Component.getLayout;	// undefined
// ...

 

이 오류는 특정 레이아웃을 사용하지 않는 페이지(getLayout을 설정하지 않은 페이지)에서는

getLayout이라는 변수에 Component.getLayout 대신 page를 그대로 리턴하는 함수를 넣어 예외처리를 통해 해결할 수 있다.

// _app.tsx

// ...
const getLayout = Component.getLayout ?? ((page: ReactNode) => page);
// ...

 

 

 

 

타입스크립트까지 적용하면

import GlobalLayout from "@/components/global-layout";
import "@/styles/globals.css";
import { NextPage } from "next";
import type { AppProps } from "next/app";
import { ReactNode } from "react";

type NextPageWithLayout = NextPage & {
  getLayout?: (page: ReactNode) => ReactNode;
};

export default function App({
  Component,
  pageProps,
}: AppProps & {
  Component: NextPageWithLayout;
}) {
  const getLayout = Component.getLayout ?? ((page: ReactNode) => page);

  return <GlobalLayout>{getLayout(<Component {...pageProps} />)}</GlobalLayout>;
}

 

 

 

 

 

 

 


참고자료

🔗 [이정환] 한입 크기로 잘라먹는 Next.js(15+) 

반응형
프로필사진
개발자 삐롱히

프론트엔드 개발자 삐롱히의 개발 & 공부 기록 블로그