CRA(--template typescript)에서 alias 별칭 안되는 문제 eject 해보기

    반응형

    CRA Alias

     

    친구의 청첩장을 만드는 프로젝트에서 다른 경로에 있는 모듈을 import 문법을 통해서 가져올 때 ../../../  와 같이 상대 경로로 하게 되면 너무 길어지는 문제가 있었다. 혼자 강의를 들으며 이러한 문제를 적용하는 방법을 알았기에 당연하게 tsconfig.json 파일에 paths, baseUrl 속성을 넣으면 해결될 줄 알았는데 해결되지 않았다. 처리했던 방법에 대해 정리하고 공부하려고 한다.

     

    문제


    CRA 환경에서 alias 별칭을 사용해서 절대 경로로 import 문법을 표현하려 했으나 제대로 적용되지 않고 " Module not found: Error ~~ "를 마주하는 문제가 발생했다. 기억에 남아있던 방식으로 tsconfig.json에 paths와 baseUrl을 추가하여 해결할 수 있다고 생각했지만 제대로 되지 않았다. 

     

    원인


    1. CRA(Create-react-app)의 특징

    CRA 는 설정 없는 개발 환경을 제공하기 위해 설계되었다. 그렇기 때문에 "npx create-react-app" 명령어를 통해 React 프로젝트를 시작하게 되면 몇몇 설정들은 내부로 숨겨지고 필요한 것들만 외부로 보여 폴더나 파일의 구조를 하고 있다. 그래서 설치를 해보면 아래와 같은 폴더의 구조를 하고 있다.

     

    CRA --template typescript

     

    번들링을 담당하는 webpack과 같은 설정들은 사용자가 볼 수 없도록 숨겨져 있는데 설정의 일관성, 보안을 위해 이렇게 설정되어 있다. CRA를 활용하지 않고 직접 React 프로젝트를 생성한다면 webpack 설정을 통해 별칭을 설정할 수 있지만 CRA에서는 내부적으로 숨겨진 설정들을 오버라이드 할 수 있는 여러 도구들을 사용해야 한다.

     

    2. tsconfig.json 보다 우선시 되는 webpack 설정

    일반적인 typescript 프로젝트에서 paths, baseUrl 을 사용하면 절대 경로 설정이 가능하다. typscript의 컴파일러에 의해 절대 경로로 설정이 가능하지만 CRA에서는 tsconfig.json 보다 내부적으로 숨겨져 있는 webpack 설정이 우선시 된다. 간단한 실험으로 이를 확인할 수 있는데 CRA 설치 후에 tsconfig.json의 paths와 baseUrl을 사용해서 적용해 보면 바로 " Module not found: Error ~~ "를 마주한다. 즉 tsconfig.json의 설정보다 webpack의 설정이 우선시 된다는 것이다. 

     

    해결방법


    1. CRA 설정 오버라이드

    CRACO, react-app-rewired 와 같은 라이브러리들을 사용해서 내부적으로 숨겨져 있는 webpack 설정들을 덮어쓰는 방식이다. 간편하기 때문에 많은 곳에서 추천하는 방식 중 하나이다. CRA의 설정 없는 개발 환경의 철학을 지키면서 여러 도구를 통해 커스터마이징이 가능하기 때문이다.

     

    2. eject 하는 방법

    eject는 말 그대로 튀어나오게 하다. 와 같다. 내부적으로 숨겨져 있던 여러 설정 파일들을 사용자가 변경할 수 있도록 보이게 하는 작업인데 CRA를 설치한 후 "npm run eject" 명령어를 통해 실행 할 수 있다. 명령어를 실행하면 정말 eject 할 것인지 물어보는데 동의하고 명령어를 실행하면 된다. 

     

    CRA eject후, CRA eject 전

     

    CRA의 eject 한 후와 전의 구조 차이이다. 보면 config 파일이 추가적으로 생긴 것을 확인할 수 있다. 여기서 webpack.config.js 파일을 수정하면 오버라이드 도구를 사용하지 않고 alias 별칭을 사용할 수 있다. webpack 공식 문서에 자세한 설명이 나와있는 것처럼 resolve 객체를 찾아 alias를 추가해 주면 된다. 

     

    resolve: {
          // This allows you to set a fallback for where webpack should look for modules.
          // We placed these paths second because we want `node_modules` to "win"
          // if there are any conflicts. This matches Node resolution mechanism.
          // https://github.com/facebook/create-react-app/issues/253
          modules: ["node_modules", paths.appNodeModules].concat(
            modules.additionalModulePaths || []
          ),
          // These are the reasonable defaults supported by the Node ecosystem.
          // We also include JSX as a common component filename extension to support
          // some tools, although we do not recommend using it, see:
          // https://github.com/facebook/create-react-app/issues/290
          // `web` extension prefixes have been added for better support
          // for React Native Web.
          extensions: paths.moduleFileExtensions
            .map((ext) => `.${ext}`)
            .filter((ext) => useTypeScript || !ext.includes("ts")),
          alias: {
            // Support React Native Web
            // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
            "react-native": "react-native-web",
            // Allows for better profiling with ReactDevTools
            ...(isEnvProductionProfile && {
              "react-dom$": "react-dom/profiling",
              "scheduler/tracing": "scheduler/tracing-profiling",
            }),
            ...(modules.webpackAliases || {}),
            "@": path.resolve(__dirname, "../src"),
            "@components": path.resolve(__dirname, "../src/components"),
            // 필요한 다른 별칭들 추가
          },
          plugins: [
            // Prevents users from importing files from outside of src/ (or node_modules/).
            // This often causes confusion because we only process files within src/ with babel.
            // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
            // please link the files into your node_modules/ and let module-resolution kick in.
            // Make sure your source files are compiled, as they will not be processed in any way.
            new ModuleScopePlugin(paths.appSrc, [
              paths.appPackageJson,
              reactRefreshRuntimeEntry,
              reactRefreshWebpackPluginRuntimeEntry,
              babelRuntimeEntry,
              babelRuntimeEntryHelpers,
              babelRuntimeRegenerator,
            ]),
          ],
        },

     

    CRA에 webpack.config.js의 resolve 객체 내부에 alias에 사용할 경로들을 추가해 주고 tsconfig.json에도 pahts, baseUrl을 웹팩 설정과 일치하도록 적어주면 된다. 이후에도 모듈을 찾을 수 없다는 에러가 발생하면 node_modules 폴더를 삭제하고 다시 설치해 주면 된다. 

     

    eject 방법을 추천하지 않는 이유


    1. CRA의 철학 유지

    eject를 하게되면 내부적으로 숨겨져 있던 설정들이 모두 보이기 때문에 설정 없는 개발 환경이라는 유지와 맞지 않게 된다. CRACO와 같은 오버라이드 도구들은 CRA의 철학을 유지하면서도 원하는 설정을 커스터마이징 할 수 있다.

     

    2. 프로젝트의 복잡성, 유지보수성

    eject는 한번 하게 되면 되돌릴 수 없다. 또한 프로젝트를 할 때 많은 팀원들이 설정해야 하고, 내부적으로 숨겨져 있던 설정들이 보이기 때문에 직접 관리해야 하고 복잡성이 늘어나게 된다. 

    반응형

    댓글