✏️ Next.js

[pages] 기존 React 프로젝트 Next.js 로 마이그레이션하기

category
✏️ Next.js
date
thumbnail
slug
next-js-기존-react-프로젝트-next-js로-마이그레이션하기
author
status
Public
tags
pages router
summary
type
Post
 
Next.js 를 공부하면서 기존에 React로 만들었던 프로젝트를 Next.js로 마이그레이션해보고 싶어졌다!
 
이번 포스트에서 마이그레이션하는 프로젝트는 아래 사이트이다.
팀 프로젝트를 하며 커밋 메세지의 컨벤션을 정할 때 깃모지를 사용하기로 했으나 공식 깃모지 페이지에는 너무나 많은 이모지를 사용하며 영어로 설명이 되어 있어 팀원들이 사용하기에 불편함을 느꼈고, 그렇기 때문에 팀원들과 사용하기 위해 만든 사이트이다.
처음에는 팀원들과 사용하려고 만들었지만 점점 주위 사람들도 사용하게 되었고, 이후에는 아예 모르는 분들도 사용하고 있다.
사용자의 피드백을 받아 이모지를 추가해본 경험이 있으며, 추후에는 다른 기능들도 넣고 싶은 그런 애착이 가는 프로젝트다.
 
먼저, 프로젝트의 기존 폴더구조는 다음과 같다.
notion image
 
 
이제 Next.js로 마이그레이션을 해보자.
마이그레이션 작업에 들어가기에 앞서,
Next.js는 13버전 pages route 방식을 사용할 예정이며 패키지 매니저로는 npm을 사용한다.
 

마이그레이션

1. react-scripts 제거

npm remove react-scripts
 

2. next 설치

npm install next
 

3. package.json scripts 수정

react-scripts를 통해 실행하던 명령어들을 next를 통해 실행하도록 한다.
notion image
 

4. .gitignore 수정

.next 를 추가해서 next 패키지 관련 폴더가 깃에 추가되지 않도록 설정한다.
 
notion image
notion image
 

5. pages 폴더 생성 후 public 폴더에 있던 index.html 파일 분리하기

public 폴더에 있던 index.html 파일을 분리해서 pages/_document.jspages/_app.js 로 옮긴다.
물론 _document.js 파일과 _app.js 파일은 새로 생성한다.
 
공식문서에서 제공한 _document.js 구조와 _app.js 구조
import Document, { Html, Head, Main, NextScript } from 'next/document';
 
class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const originalRenderPage = ctx.renderPage;
 
    // Run the React rendering logic synchronously
    ctx.renderPage = () =>
      originalRenderPage({
        // Useful for wrapping the whole react tree
        enhanceApp: (App) => App,
        // Useful for wrapping in a per-page basis
        enhanceComponent: (Component) => Component,
      });
 
    // Run the parent `getInitialProps`, it now includes the custom `renderPage`
    const initialProps = await Document.getInitialProps(ctx);
 
    return initialProps;
  }
 
  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}
 
export default MyDocument;
공식문서에서 제공한 Custom Document (_document.js) 구조
 
export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}
공식문서에서 제공한 Custom App (_app.js) 구조
 
 
아래 코드는 기존 React 프로젝트에서의 index.html 파일이다.
이를 분리해서 DocumentApp을 커스텀해보자.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />

    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />

    <meta content="😉 Easy Gitmoji" property="og:title" />
    <meta content="https://easy-gitmoji.halamlee.com/" property="og:url" />
    <meta content="쉽게 Gitmoji 사용해요" property="og:description" />
    <meta content="%PUBLIC_URL%/meta-img.png" property="og:image" />

    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <link
      href="//spoqa.github.io/spoqa-han-sans/css/SpoqaHanSansNeo.css"
      rel="stylesheet"
      type="text/css"
    />
    <title>Easy Gitmoji</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>
기존 React 프로젝트에서의 index.html
 

5-1. styled-components 적용

잠시 styled-components 적용을 위한 설명을 하고자 한다.
프로젝트 루트 경로에서 next.config.js 파일을 만들고 아래와 같이 설정한다.
// next.config.js

const nextConfig = {
  compiler: {
    styledComponents: true,
  },
};

module.exports = nextConfig;
 
 
다시 이어서 하던 과정을 하자면…
아래는 기존 index.html 파일을 분리해서 만든 pages/_document.js 파일이다.
index.html에서 <head> 부분을 가져왔지만, <meta> 태그 중 viewport와 관련된 코드와 <title> 태그는 이곳이 아니라 _app.js 에 넣어주어야 한다.
또한 해당 프로젝트에서는 styled-components를 사용했기 때문에 코드가 조금 다르다.
처음에는 styled-components를 신경쓰지 않고 수정했더니 초기 html 코드만 렌더링이 되고, 스타일 컴포넌트의 적용이 늦게 되는 현상이 있었다. 이를 해결하기 위해 아래 코드로 수정해서 해결했다.
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheet } from 'styled-components';

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    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>
        <Head>
          <meta charSet="utf-8" />
          <link rel="icon" href="/favicon.ico" />

          <meta name="theme-color" content="#000000" />
          <meta
            name="description"
            content="Web site created using create-react-app"
          />

          <meta content="😉 Easy Gitmoji" property="og:title" />
          <meta
            content="https://easy-gitmoji.halamlee.com/"
            property="og:url"
          />
          <meta content="쉽게 Gitmoji 사용해요" property="og:description" />
          <meta content="/meta-img.png" property="og:image" />

          <link rel="apple-touch-icon" href="/logo192.png" />
          <link rel="manifest" href="/manifest.json" />
          <link
            href="//spoqa.github.io/spoqa-han-sans/css/SpoqaHanSansNeo.css"
            rel="stylesheet"
            type="text/css"
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}
기존 index.html 파일을 분리해서 만든 pages/_document.js
 
아래는 index.html에서의 <head> 태그 내용물 중 viewport와 관련된 <meta> 태그와 <title> 태그이다.
이 코드들을 _app.js 에 넣지 않으면 에러가 생긴다.
import Head from 'next/head';
import '../styles/global.css';

export default function MyApp({ Component, pageProps }) {
  return (
    <>
      <Head>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Easy Gitmoji</title>
      </Head>
      <Component {...pageProps} />
    </>
  );
}
 
 
 

6. styles/global.css 생성

프로젝트 루트 경로에 styles 폴더를 생성한 후 그 안에 global.css 파일을 만든다.
그리고 React 프로젝트에서 만들었던 App.css 라던가 index.css 등 전체적으로 적용하는 css 파일의 코드를 넣는다.
만든 global.css 파일을 _app.js에서 import한다.
아래 코드는 위 _app.js 코드와 동일하며 상단에 있는 import ‘../styles/global.css’; 가 이 과정에 해당된다.
import Head from 'next/head';
import '../styles/global.css';

export default function MyApp({ Component, pageProps }) {
  return (
    <>
      <Head>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Easy Gitmoji</title>
      </Head>
      <Component {...pageProps} />
    </>
  );
}
 
 
 

7. React 프로젝트의 App.jsx 파일을 pages/index.jsx 로 변경

React는 App.jsx 파일이 기본으로 보여주는 파일이지만, Next.js에서는 pages 폴더 안에 있는 index.jsx 파일이 기본적으로 보여주는 파일이다. 따라서 마이그레이션을 하며 이전과 같이 기본적으로 보여주려는 파일을 pages 폴더 안에 index.jsx 파일을 만들고 붙여넣는다.
 
notion image
 
 

8. Next.js 실행 후 오류 해결

이제 터미널에 npm run dev 명령어를 입력해서 Next.js 로 마이그레이션한 프로젝트를 실행할 수 있다.
localhost:3000 으로 들어가서 결과를 볼 수 있으며, 이때 React와 Next에서 사용하는 방식이 달라 생기는 오류들이 생길 수 있다. 이를 해결하면 된다.
ex) <image><Image>
 
 

결과

React 프로젝트에서 Next로 마이그레이션을 하며 수정/추가/삭제를 한 파일은 아래와 같다.
notion image
 
아래는 마이그레이션 전과 후 폴더구조이다.
React.js (마이그레이션 전)
React.js (마이그레이션 전)
Next.js (마이그레이션 후)
Next.js (마이그레이션 후)
 
 
 
 

참고