본문 바로가기

Next.js

[Next.js] 커스텀 App & Document

Custom App

Next.js 에서는 App 컴포넌트를 사용하여 페이지를 초기화합니다.
이를 재정의하면 페이지 초기화 시 다양한 작업을 수행할 수 있습니다.
예를 들어:

  • 공통 레이아웃을 생성할 수 있습니다.
  • 페이지에 추가적인 데이터를 주입할 수 있습니다.
  • 전역 CSS 를 추가할 수 있습니다.

사용 방법

기본 App 컴포넌트를 제정의하려면 pages/_app 파일을 아래와 같이 생성합니다.

// pages/_app.tsx
import type { AppProps } from 'next/app';

export default function MyApp({ Component, pageProps }: AppProps) {
    return <Component {...pageProps} />;
}
  • Component 속성은 현재 활성 페이지를 나타내며, 경로 간 이동 시 Component 는 새로운 페이지로 변경됩니다. 따라서 Component 에 전달하는 모든 props 는 해당 페이지에서 받을 수 있습니다.
  • pageProps 는 Next.js 의 데이터 페칭 메서드에 의해 미리 로드된 초기 props 를 포함하는 객체입니다. 데이터가 없을 경우 빈 객체가 됩니다.

App 컴포넌트에서는 Next.js 의 데이터 페칭 메서드 (getStaticProps, getServerSideProps) 를 사용할 수 없습니다.

getInitialProps 와 App

App 컴포넌트에서 getInitialProps 를 사용하면 getStaticProps 가 없는 페이지에 대해 자동 정적 최적화가 비활성화됩니다. 이 패턴은 권장되지 않지만, 점진적으로 App 라우터를 도입해 페이지와 레이아웃의 데이터를 쉽게 가져오는 것이 좋습니다.

// pages/_app.tsx
import App, { AppContext, AppInitialProps, AppProps } from 'next/app';

type AppOwnProps = { example: string };

export default function MyApp({
  Component,
  pageProps,
  example,
}: AppProps & AppOwnProps) {
  return (
    <>
      <p>Data: {example}</p>
      <Component {...pageProps} />
    </>
  );
}

MyApp.getInitialProps = async (
  context: AppContext
): Promise<AppOwnProps & AppInitialProps> => {
  const ctx = await App.getInitialProps(context);

  return { ...ctx, example: 'data' };
};
  • MyApp 컴포넌트: Component, pageProps, 추가 속성을 받아서 렌더링합니다.
  • getInitialProps 함수: App 컴포넌트에서 데이터를 가져오며, 이를 통해 페이지 전역 데이터를 제공할 수 있습니다. 이 함수는 context 객체를 받아 필요한 데이터를 가져옵니다.

Custom Document

커스텀 Document 를 사용하면 페이지를 렌더링할 때 사용되는 <html> 및 <body> 태그를 업데이트할 수 있습니다.

기본 Document 를 재정의하려면 아래와 같이 pages/_document 파일을 생성합니다.

// pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html lang="en">
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

• _document는 서버에서만 렌더링되므로, onClick 같은 이벤트 핸들러를 사용할 수 없습니다.
• <Html>, <Head>, <Main>, <NextScript>는 페이지가 제대로 렌더링되기 위해 필요합니다.

  • _document 에서 사용되는 <Head> 컴포넌트는 next/head 와 다릅니다. 여기서 사용하는 헤드 컴포넌트는 모든 페이지에 공통으로 들어가는 <head> 코드를 위해서만 사용해야 합니다. <title> 태그와 같은 경우는 페이지나 컴포넌트에서 next/head 를 사용하는 것이 좋습니다.
  • <Main> 바깥의 React 컴포넌트는 브라우저에서 초기화되지 않습니다. 여기서 애플리케이션 로직이나 커스텀 CSS 를 추가하면 안됩니다. 모든 페이지에 공통된 컴포넌트 (메뉴, 툴바 등) 가 필요하다면, Layouts 를 참조하세요.
  • Document 는 현재 getStaticProps 나 getServerSideProps 와 같은 Next.js 데이터 페칭 메서드를 지원하지 않습니다.

renderPage 커스터마이징

renderPage 커스터마이징은 고급 기능으로, CSS-in-JS 라이브러리가 서버 사이드 렌더링을 지원하도록 하는 데 필요합니다. 내장된 styled-jsx 지원에는 필요하지 않습니다.

이 패턴의 사용은 권장되지 않습니다. 대신, App 라우터를 점진적으로 도입하여 페이지와 레이아웃의 데이터를 더 쉽게 가져오는 방법을 고려하는 것이 좋습니다.

// pages/_document.tsx
import Document, {
  Html,
  Head,
  Main,
  NextScript,
  DocumentContext,
  DocumentInitialProps,
} from 'next/document';

class MyDocument extends Document {
  static async getInitialProps(
    ctx: DocumentContext
  ): Promise<DocumentInitialProps> {
    const originalRenderPage = ctx.renderPage;

    // React 렌더링 로직을 동기적으로 실행
    ctx.renderPage = () =>
      originalRenderPage({
        // 전체 React 트리를 래핑하는 데 유용
        enhanceApp: (App) => App,
        // 페이지별로 래핑하는 데 유용
        enhanceComponent: (Component) => Component,
      });

    // 부모 `getInitialProps`를 실행하고, 이제 커스텀 `renderPage`가 포함됨
    const initialProps = await Document.getInitialProps(ctx);

    return initialProps;
  }

  render() {
    return (
      <Html lang="en">
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

• _document의 getInitialProps는 클라이언트 측 전환 중에 호출되지 않습니다.
• _document의 ctx 객체는 getInitialProps에서 받는 것과 동일하며, 여기에 renderPage가 추가로 포함됩니다.


결론

커스텀 App

Next.js에서 커스텀 App 컴포넌트를 사용하면 애플리케이션 초기화 시 다음과 같은 작업을 수행할 수 있습니다:

  • 공통 레이아웃 생성: 모든 페이지에 적용될 공통 레이아웃을 정의할 수 있습니다.
  • 추가 데이터 주입: 페이지 초기화 시 필요한 추가 데이터를 전역적으로 주입할 수 있습니다.
  • 전역 CSS 추가: 애플리케이션 전체에 적용되는 전역 CSS를 설정할 수 있습니다.

주요 포인트:

  • pages/_app.tsx 파일을 생성하여 커스텀 App 컴포넌트를 정의합니다.
  • ComponentpageProps를 사용하여 현재 페이지와 페이지 props를 전달합니다.
  • App 컴포넌트에서는 getStaticPropsgetServerSideProps를 사용할 수 없으며, getInitialProps를 사용하면 자동 정적 최적화가 비활성화됩니다.

권장 사항:

  • 페이지에서 직접 데이터 페칭 메서드를 사용하고, App 라우터를 도입하여 데이터 페칭을 쉽게 관리하는 것이 좋습니다.

커스텀 Document

커스텀 Document를 사용하면 페이지를 렌더링할 때 사용되는 <html><body> 태그를 업데이트할 수 있습니다. 이를 통해 전역적인 HTML 구조와 메타 정보를 설정할 수 있습니다.

주요 포인트:

  • pages/_document.tsx 파일을 생성하여 커스텀 Document 컴포넌트를 정의합니다.
  • <Html>, <Head>, <Main>, <NextScript> 컴포넌트를 사용하여 페이지가 제대로 렌더링되도록 설정합니다.
  • _document는 서버에서만 렌더링되므로 이벤트 핸들러를 사용할 수 없습니다.

주의사항:

  • <Head> 컴포넌트는 모든 페이지에 공통으로 들어가는 코드를 위해 사용해야 하며, 페이지별 메타 정보는 next/head를 사용하는 것이 좋습니다.
  • <Main> 바깥의 React 컴포넌트는 브라우저에서 초기화되지 않으므로, 여기서 애플리케이션 로직이나 커스텀 CSS를 추가하지 않아야 합니다.
  • Document는 현재 getStaticPropsgetServerSideProps와 같은 데이터 페칭 메서드를 지원하지 않습니다.

결론적으로, 커스텀 App과 커스텀 Document를 활용하면 Next.js 애플리케이션의 초기화와 전역 설정을 효율적으로 관리할 수 있습니다.

'Next.js' 카테고리의 다른 글

[Next.js] 자동 정적 최적화  (0) 2024.08.22