티스토리 뷰

최근에 CRA없이 webpack을 직접 설정하거나 Vite를 사용했기 때문에 CRA의 경우는 따로 webpack 설정을 해본 적이 없다. 또한, CRA는 따로 보일러 플레이팅 작업을 하지 않아도 이미 세팅되어 있기 때문에 여기서 더 건들이는 것보단 있는 그대로 사용한 경우가 대다수였다.

이번에 원티드 사전 과제를 진행하면서 프로젝트 조건으로 CRA 사용이 있었고 여기서 나는 typescript를 적용했기 때문에 웹팩과 타입스크립트 config만 수정해주면 된다고 생각했다. 하지만, CRA를 사용하면서 웹팩의 config는 본적이 없다. 그 이유는 CRA에 설정파일이 숨겨져 있기 때문이다. 

  • CRA는 내부적으로 프로젝트 디렉토리를 간결하게 유지하기 위해 웹팩 설정(webpack config)나 script 폴더들을 숨겨놓기 때문이다. 
  • 이 내용을 수정하기 위해서는 숨겨진 파일을 외부로 노출시키는 작업이 필요한데, 이때 사용하는 명령어가 `eject`명령어이다. 

CRA로 만든 프로젝트의 package.json 파일의 내용을 보면 아래와 같은 명령어가 기본 세팅되어있음을 볼 수 있다.

//package.json

"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  • `yarn eject` 혹은 `npm run eject` 로 eject 명령어를 실행시키면 webpack, babel 등의 설정 파일들을 추출할 수 있다. 
💥하지만, eject명령어로 추출된 폴더 및 파일들은 이제 디렉토리 상에 노출되며, 이전 상태로 돌아갈 수 없다는 단점이 있다.

eject 명령어 시 노출되는 것들

  • 설정관련 config 파일 
  • script 폴더 및 package.json의 dependency
  • babel 및 jest 설정코드
💥또한, 한 번 eject를 하면 React APP의 버전업에 대한 구성요소 업그레이드를 할 수 없다

 

위와 같은 이유로 webpack의 config 를 변경할 때, 직접 CRA의 세팅을 노출시켜 직접 수정하는 방법(eject명령어)보다는 필요한 설정만을 overriding 하여 사용하는 것이 좋을 거 같다.

 

그리고 이미 많은 개발자들이 사용하고 있는 패키지 중 하나인 Craco를 알게 됐다. 

Craco 로 CRA 웹팩 설정 오버라이드해보기(alias 설정)

Craco란 Create-React-App Configuration Override 의 약어이다. => CRA에서 config 설정을 덮어쓰기 위한 패키지

처음 craco를 알게됐을 땐, 악어 마크가 있을 줄 알았는데 그냥 곧이 곧대로의 작명이었어서 살짝 실망했다.

  • 루트 경로(폴더 최상단)에 `craco.config,js`를 추가함으로써 eslint, babel 등을 쉽게 설정할 수 있다.
  • 내 경우 alias 세팅을 해주고 싶었던 것이므로 craco와 더불어 alias 관련 패키지를 추가로 설치해줘야 한다. 
2023 06 10 기준, craco-alias는 deprecated(안 쓰임)됐으며 대신에 react-app-alias 를 설치해주면 된다. 
출처: craco-alias repo
yarn add -D @craco/craco react-app-alias

위의 명령어를 통해서 craco와 alias 플러그인을 추가로 설치해준다.

그 다음, 아래의 파일을 만들거나 수정해주면 된다.

 

craco.config.ts (루트 경로에 파일생성)

  • 직접 생성해야 하는 파일
const { CracoAliasPlugin } = require("react-app-alias");

module.exports = {
  plugins: [
    {
      plugin: CracoAliasPlugin,
      options: {
        source: "tsconfig",
        baseUrl: "./src",
        tsConfigPath: "./tsconfig.paths.json",
      },
    },
  ],
};
2023 06 12, craco.config의 baseUrl은 tsconfig.paths.json의 baseUrl과 같아야 한다. 
tsconfig.paths에 baseUrl이 있으니 craco.config에서는 빼도되지 않을까? 하고 주석처리를 봤는데 바로 alias로 설정한 경로를 못 찾는 에러가 발생했다. 둘이 통일시켜주고 나서야 제대로 동작했다.  

tsconfig.paths.json

  • 직접 생성해야 하는 파일
  • 여담) .json의 확장자는 생략해도되는 것으로 알고 있는데 찜찜하면 그냥 풀네임을 작성해주면 된다. 
{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@components/*": ["./components/*"],
      "@hooks/*": ["./hooks/*"],
      "@pages/*": ["./pages/*"],
      "@types/*": ["./types/*"],
      "@utils/*": ["./utils/*"],
      "@layouts/*": ["./layouts/*"],
      "@contexts/*": ["./contexts/*"]
    }
  }
}

tsconfig.json

  • 기존에 있던 파일
{
  "extends": "./tsconfig.paths.json",
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": ["src"]
}
  • 기존에 있던 settings들에 최상단의 extends 코드만 추가해주었다. => tsconfig.paths.json에 기입된 내용이 기존 ts config에 적용될수 있도록 연결 

이렇게 세팅 파일들을 만들어주고 마지막으로 package.json의 script 명령어를 craco로 아래와 같이 수정해주면 된다.

 

package.json 

  • 기존에 있던 파일
"scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "react-scripts eject"
  },
  • 공식 홈페이지에서는 start, build, test 명령어를 react-script 에서 craco로 변경하라고 명시되어 있어서 나도 3가지 명령어를 craco CLI로 변경해주었다. 

 

덕분에 아래와 같이 예쁜 모습으로 import할 수 있게 됐다. 

App.tsx 

import React, { Suspense } from "react";
import "./App.css";
import { Routes, Route, Navigate } from "react-router-dom";

const LogIn = React.lazy(() => import("@pages/Login"));
const SignUp = React.lazy(() => import("@pages/SignUp"));
const GeneralLayout = React.lazy(() => import("@layouts/GeneralLayout"));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/login" element={<LogIn />} />
        <Route path="/signup" element={<SignUp />} />
        <Route path="/content" element={<GeneralLayout />} />
        {/* 기본 redirect */}
        <Route path="/" element={<Navigate to="/login" />} />
        {/* 없는 페이지 접근 */}
        <Route path="*" element={<p>404 Not Found</p>} />
      </Routes>
    </Suspense>
  );
}

export default App;

 

아무것도 모르던 시절에는 그냥 갖다 쓰면 되겠지~ 하고 CRA를 썼었는데 사실 그 내부적으로 엄청나게 귀찮은 세팅들이 미리 되어있었고, 그 덕분에 CRA이 너무 무겁기도 했다. alias 경우에는 depth가 그리깊지 않고 프로젝트 규모가 작다면 굳이 할 필요없는 작업이겠지만 언제 CRA로 또 작업을 할지도 모르고.. 무엇보다 경로가 깔끔하게 나와있어야 어떤 컴포넌트가 어디서 왔는지 좀 더 명시적일 것이고 무엇보다 보기에 예쁘다(?). 

vscode의 플러그인을 설치할 때도 뭐가 잘못될까 두려워서 불편해도 기존 세팅들을 그냥 썼었는데 결국 나중에는 반복되는 작업이 너무 귀찮아서 찾아 쓰게 된다. 여러가지를 배우면서 우선 써보고 아니면 말고 식의 태도가 생긴거 같아서 본인에게도 좀 의아하다. 역시 직접 경험해보는 게 제일 좋다. 

2023 06 12 후기 추가) CRA + craco(webpack alias 설정 오버라이드) 조합이 마냥 좋은 것은 아닌 거 같다. 우선 HMR가 동작하지 않는다. HMR는 간단히 말하면 코드가 수정되면 새로고침없이 바로 브라우저에 변화를 볼 수 있는 기능이다. 항상 동작했기 때문에 별로 편의성을 못느끼고 있었는데 막상 안되니 매우 불편하다. craco조합을 쓰면 매번 새로고침을 해야 화면에 반영이 된다. 

 

댓글