CRA eject 하지 않고 optional chaining 사용하기

CRA 환경을 eject 하지 않고 Optional chaining을 사용할 수 있게 설정합니다. 아래 예시처럼 번잡했던 코드가 간결해집니다.

let nestedProp = obj.first && obj.first.second

// Optional chaining
let nestedProp = obj.first?.second

eject 하지 않아도 react-app-rewired, customize-cra 모듈을 이용해서 CRA 환경을 override 할 수 있습니다. 우선 아래 패키지들을 설치합니다.

npm i -D react-app-rewired customize-cra

optional-chaining 플러그인도 설치해줍니다.

npm i -D @babel/plugin-proposal-optional-chaining

package.json 의 scripts에서 기존의 react-scripts로 되어 있는 부분을 react-app-rewired로 변경합니다.

"scripts": {
  "start": "react-app-rewired start",
  "build": "react-app-rewired build"
}

config-overrides.js 파일을 생성해서 아래와 같이 override 하는 코드를 작성합니다. .babelrc 파일을 사용하는 방식입니다.

const { useBabelRc, override } = require('customize-cra')

module.exports = override(
  useBabelRc(),
)

.babelrc 파일을 생성해서 optional-chaining 플러그인 설정을 추가합니다.

{
  "plugins": ["@babel/plugin-proposal-optional-chaining"]
}

이렇게 설정이 완료됐습니다.

VSCODE에서 optional chaining을 사용했는데 문제라고 표시된다면 아래처럼 프로젝트 지역설정으로 .vscode/settings.json 파일을 만들어서 “javascript.validate.enable”: false 를 추가하세요.

{
  "javascript.validate.enable": false
}

사실 이 글은 Optional chaining 추가하는 것보다는 CRA를 eject 없이 커스텀 한다는 내용인데요. eject 하지 않으면 많은 개발자에게 익숙한 개발환경을 유지하면서 업데이트도 편하게 할 수 있습니다. https://github.com/arackaf/customize-cra/blob/HEAD/api.md 에서 customize-cra의 다른 API들도 확인할 수 있습니다.

저는 웬만하면 이렇게 override 해서 사용하는 것을 선호합니다. 그게 어렵다면 CRA가 아니라 직접 환경을 만드는 방법으로 했겠지요.

아래는 몇 가지 설정을 더 추가해서 사용한 config-overrides.js 내용입니다.

const path = require('path')
const { useBabelRc, addWebpackAlias, addBundleVisualizer, override } = require('customize-cra')
const rewireReactHotLoader = require('react-app-rewire-hot-loader-for-customize-cra')

module.exports = override(
  useBabelRc(),
  rewireReactHotLoader(),
  addWebpackAlias({
    'react-dom': '@hot-loader/react-dom',
    'react-dom$': 'react-dom/profiling',
    'scheduler/tracing': 'scheduler/tracing-profiling',
    '~': path.resolve(__dirname, './src')
  }),
  addBundleVisualizer({
    analyzerMode: 'static',
    reportFilename: 'report.html'
  }, true),
)