본문 바로가기
Web/Next.js

Next.js 13 pages 폴더에서 app router로 마이그레이션하기

by 김첨지 2023. 1. 29.

 

next.js 13 migration from pages router to app router

 
 

app router

next.js 13 버전이 도입되면서 기존 pages 폴더와는 다르게 동작하는 app router가 생겼다.
app 디렉토리는 아직 베타버전이지만, 새로 등장한 기능들을 사용하기 위해선 앱 디렉터리를 사용해야 한다.
pages 라우터와 app라우터의 가장 큰 차이점은 app에선 기본적으로 server component가 적용된다는 것이다.
 
 

pages 폴더에서 app 폴더로 마이그레이션

1. next.config.js 코드 추가

app directory는 아직 베타버전으로, 사용하기 위해선 next.config.js에 아래 코드를 추가해줘야 한다.

/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    appDir: true,
  },
};

module.exports = nextConfig;

 
 

2. app directory 생성

루트 폴더 혹은 src폴더에 app 폴더 추가

루트 폴더 혹은 src폴더에 app 디렉토리 생성

 
 

3. next/head 마이그레이션

https://jha-memo.tistory.com/97

next.js 13 pages 폴더에서 app 구조로 마이그레이션하기 -  next/head

next/head migration pages 디렉토리에서는 next/head를 이용하여 meta, title와 같은 html 요소를 관리하였다. // pages/index.tsx import Head from 'next/head' export default function Page() { return ( ) } app 디렉토리에서는 이것

jha-memo.tistory.com

 
 

4. 루트 폴더에 layout.tsx 파일 생성 후 _document.tsx, _app.tsx 제거

제거하기 전 _document.tsx, _app.tsx에서 필요한 코드들은 layout.tsx로 옮겨준다.

export default function RootLayout({
  // Layouts must accept a children prop.
  // This will be populated with nested layouts or pages
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );

 
기본적으로 layout.tsx는 위 코드만으로도 작동하지만

// pages/_app.tsx


import { theme } from "@chooz/ui";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import type { AppProps } from "next/app";
import styled, { ThemeProvider } from "styled-components";
import { GlobalStyles } from "styles/globalStyles";
import Header from "../components/header/Header";

function App({ Component, pageProps }: AppProps) {
  const queryClient = new QueryClient();
  return (
    <QueryClientProvider client={queryClient}>
      <ReactQueryDevtools />
      <ThemeProvider theme={theme}>
        <GlobalStyles />
        <div id="stars" />
        <div id="stars2" />
        <div id="stars3" />
        <Applayout>
          <Header />
          <Component {...pageProps} />
        </Applayout>
      </ThemeProvider>
    </QueryClientProvider>
  );
}

const Applayout = styled.div`
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  padding: 0 16px;
  flex: 1;
`;

export default App;

 

// pages/_document.tsx

import type { DocumentContext } from "next/document";
import Document, { Head, Html, Main, NextScript } from "next/document";
import { ServerStyleSheet } from "styled-components";

export default class MyDocument extends Document {
  // Styled-components
  static async getInitialProps(ctx: DocumentContext) {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
        });

      const initialProps = await Document.getInitialProps(ctx);
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      };
    } finally {
      sheet.seal();
    }
  }

  render() {
    return (
      <Html lang="kr">
        <Head>
          <link
            as="style"
            href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/variable/pretendardvariable.css"
            rel="stylesheet"
          />
          <link
            as="style"
            href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendardstatic.css"
            rel="stylesheet"
          />
        </Head>
        <body>
          <div id="portal" />
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

 
위와 같이 document와 app에서 사용하던 코드들이 있어 이것들을 layout.tsx에 하나로 합쳐야 했다.

// app/layout.tsx

"use client";

import { theme } from "@chooz/ui";
import styled, { ThemeProvider } from "styled-components";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { GlobalStyles } from "styles/globalStyles";

function RootLayout({
  // Layouts must accept a children prop.
  // This will be populated with nested layouts or pages
  children,
}: {
  children: React.ReactNode;
}) {
  const queryClient = new QueryClient();
  return (
    <html lang="kr">
      <body>
        <div id="portal" />
        <QueryClientProvider client={queryClient}>
          <ReactQueryDevtools />
          <ThemeProvider theme={theme}>
            <GlobalStyles />
            <div id="stars" />
            <div id="stars2" />
            <div id="stars3" />
            <Applayout>
              {/* <Header /> */}
              {children}
            </Applayout>
          </ThemeProvider>
        </QueryClientProvider>
      </body>
    </html>
  );
}

const Applayout = styled.div`
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  padding: 0 16px;
  flex: 1;
`;

export default RootLayout;

위와 같이 합쳐주었다.
 
 

5. pages 폴더 이동

기존의 index.tsx는 page.tsx로 변경하였다.
자세한 사항은 아래에서 확인할 수 있다.
https://beta.nextjs.org/docs/upgrade-guide#step-4-migrating-pages

Upgrade Guide | Next.js

Learn how to upgrade your Next.js application.

beta.nextjs.org

 
공식 문서에서는 먼저 page.tsx를 만들고, 기존 컴포넌트들을 임포트해서 사용하는 것을 추천한다.
 
 

6. useRouter import 변경

https://jha-memo.tistory.com/98

next.js 13 pages 폴더에서 app 구조로 마이그레이션하기 - Routing Hooks

Migrating Routing Hooks app directory에서는 기존 next/router대신 next/navigation의 useRouter(), usePathname(), useSearchParams()를 사용한다. 만약 app 디렉토리에서 기존 next/router을 임포트할 경우 다음과 같은 에러가 발

jha-memo.tistory.com

 
 

7.  ssr방식에서 style-components  사용

https://jha-memo.tistory.com/99

next.js 13 pages 폴더에서 app 구조로 마이그레이션하기 - style-components

ssr방식에서 style-components 기존 pages 디렉토리 방식에서는 server side rendering을 위해 아래 코드를 추가하여 사용했다. // _document.tsx export default class MyDocument extends Document { static async getInitialProps(ctx: Doc

jha-memo.tistory.com

 
 
 

마치며

아직 베타버전인 app 디렉터리로 마이그레이션 하면서 수많은 에러를 마주했다.
구글을 뒤져봐도 자료가 별로 없어 공식문서를 계속 읽으면서 진행했다.
다른 사람들은 나처럼 많은 시행착오를 겪지 않기를 바라며 도움이 됐으면 한다.
 
 

Reference

https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration

Upgrading: App Router Migration | Next.js

Using App Router Features available in /app

nextjs.org