본문 바로가기

FrontEnd+

CRA 에서 next.js 로 마이그레이션하기

 

 

이미 CRA로 패키지를 구성한 경우, next.js를 덧붙이는 방법을 정리해보자.

package.json 수정

우선, react-scripts를 제거한다. react-router-dom으로 라우팅하고 있다면 이것도 제거한다. (react와 react-dom은 내비둬야한다.)
그리고 next를 설치한다.

npm uninstall react-scripts react-router-dom 
npm install next

그리고 package.json에 scripts도 수정해주자.

{ 
  "dev": "next dev", 
  "build": "next build", 
  "start": "next start", 
}

 

public으로 static assets 옮기기

CRA에서는 public 디렉토리에 HTML 엔트리 파일(index.html)을 위치시키는 반면, Next.js 에서는 이 디렉토리를 이미지, 폰트 등의 static asset을 모아두는 용도로 사용한다.

이미지, 폰트 등 static asset 이 있다면 public 디렉토리로 옮겨주자. 그리고 해당 정적파일을 import 해오던 곳에서도 경로를 알맞게 수정해주자. (../images/tooltip.svg 를 ../../public/images/tooltip.svg 로 변경)

원래있던 index.html의 내용물은 next.js에 맞게 바꾸어주어야 한다. 일단 pages 디렉토리를 생성하고 pages/_document.js와 pages/_app.js를 생성해두자.

 

pages/_document.js 생성

_document.js는 <head>에 메타태그를 설정하거나 <body>에 들어갈 내용을 적어주는 등의 용도로 사용한다. _document.js는 가장 먼저 실행되는 _app.js 다음으로 실행된다.

pages 디렉토리 내에 _document.js 파일을 생성하고 다음과 같이 작성하자.

import Document, { Html, Head, Main, NextScript } from 'next/document'; 

class MyDocument extends Document { 
  render() { 
    return ( 
      <Html lang="ko" />
        <body> 
          <Main /> 
          <NextScript /> 
        </body> 
      </Html> 
    ) 
  } 
} 

export default MyDocument

 

'next/document'에서 가져오는 Head는 'next/head'에서 가져오는 Head와 다른 컴포넌트이다. 'next/document'에서 가져오는 건 모든 페이지에 공통으로 사용되는 경우에 적용하고, 각 페이지별로 달라지는 <title>여기서만나 - 회원가입</title>과 같은 태그를 커스텀하려면 각 페이지 컴포넌트에서 'next/head'를 import해서 사용하는 것이 좋다.

 

pages/_app.js 생성

_app.js에는 내비게이션 등과 같이 모든 페이지에 공통으로 적용하고 싶은 레이아웃은 추가한다. 서버로 요청이 들어오면 _app.js에서 return 해주는 컴포넌트가 가장 먼저 실행된다.

글로벌 스타일시트를 추가하고 싶다면 pages/_app.js에 추가하면 된다. _app.js에 import 했다면 충돌을 피하기 위해 다른 파일에서 다시 import하지 않는 것이 좋다.

import '../globalStyles.css' 


export default function MyApp({ Component, pageProps }) { 
  return <Component {...pageProps} /> 
}

 

pages/index.js

마지막으로 라우팅을 위해 pages 디렉토리 내에 index.js 파일을 만든다. 원래있던 최상위 index.js나 App.js는 지워도 좋다.

CRA에서는 react router를 사용했지만, next.js에서는 서드파티 라이브러리를 사용하는 대신 자체적인 파일시스템 기반 라우팅을 제공한다. react router를 제거해서 번들사이즈도 줄어들고, 파일시스템에 따라 직관적으로 라우팅을 구성할 수 있다는 장점이 있다.

pages/about.js → /about 
pages/blog.js → /blog 
pages/index.js → /

"next/link"에서 Link 를 import하고 Route 컴포넌트를 pages 디렉토리 내에 있는 컴파일들로 변경해준다.

/* 변경 전 (React Router) */ 

import { Link } from 'react-router-dom'; 

export default function App() { 
  return <Link to="/about">About</Link> 
} 
  
/* 변경 후 (Next.js) */ 
  
import Link from 'next/link'; 
  
export default function App() { 
  return ( 
    <Link href="/about"> 
      <a>About</a> 
    </Link> 
  ) 
}

 

스타일링

next.js에서는 빌트인 CSS, SASS, CSS-in-JS(styled-jsx)를 지원한다. (단, SASS는 별도 설치가 필요하다. - 포코 감사합니다!)

CRA에서 .css 파일을 리액트 컴포넌트에서 바로 import할 수 있던 것처럼 next.js에서도 똑같이 할 수 있다. 단, CSS Modules을 사용 해야해서 filename.css로 작성했다면 filename.module.css로 변경해주어야 한다.

CSS Modules를 적용하면 컴포넌트 레벨로 CSS를 나눠서 작성하게 되고, 지역별로 고유의 클래스 이름을 자동으로 생성해서 네임스페이스를 관리하기도 쉬워진다.

클래스 이름을 다음과 같이 변경해주기 때문에 만약 클래스 이름으로 queryselector 선택자 같은데에서 사용했다면 코드를 수정해주어야 한다.

next.js의 클래스 이름

프로덕션 단계에서는 모든 CSS 모듈이 minify와 코드 스플리팅이 적용된 하나의 CSS 파일로 통합된다.

다음은 Button.js의 css 스타일 Button.module.css 를 import해서 적용한 예시이다.

import styles from './Button.module.css' 

export function Button() { 
  return ( 
    <button type="button" className={styles.error} > 
      Destroy 
    </button> 
  ) 
}

 

복수의 className을 설정하고 싶다면, classnames 라이브러리를 사용하면 편리하다.

import classNames from 'classnames'; 

... 

   <button 
     type="button" 
     className={classNames(styles.Tooltip, styles.IconOnly)} 
     aria-describedby="tooltip-adult"> 
     성인 기준 상세 안내 
   </button>

 

 

참고자료

Next.js 공식홈페이지 - Migrating from Create React App