Skip to main content

[Webpack 기반의 React App 만들기] 2. Create React App의 Webpack 및 Babel 환경 설정 (수정중)

서론

앞선 게시물에 이어, CRA 기반의 React App과 Vite 기반의 React App 비교를 위해 먼저 CRA 기반의 React App의 구조 및 내포하고 있는 설정들의 파악이 필요하다고 판단했습니다.
따라서 React App을 eject하여, 내포하고 있던 다양한 dependencies와 config들을 분석하는 시간을 먼저 갖고자 했습니다.
따라서 이번 포스트에서는 React App을 eject 했을 때 드러나는 의존성 및 설정들의 역할과, 최종적으로 그 구조를 도식화하여 살펴볼 예정입니다.

CRA eject시 드러나는 의존성들

  • CRA로 생성한 React App을 react-scripts eject 명령어를 통해 숨겨진 설정들과 의존성들을 꺼내면(eject) 다음과 같은 의존성들이 추가됨

Babel 관련

  • ES6 이상의 코드를 이전 환경에 호환되도록 변환하거나, 타입스크립트, React jsx 등 다른 문법의 코드를 변환해주는 툴체인
  • @babel/core : Babel 컴파일러의 코어
  • babel-jest : 코드 전처리(transform)를 위해 Babel을 사용하도록 하는 Jest의 플러그인
  • babel-plugin-named-asset-import : Babel plugin for named asset imports in Create React App
    • JS/CSS를 제외한 정적 자원들을 Named Import 형태로 가져올 수 있도록하는 플러그인
      • Named Import: default export가 아닌, named Export된 요소들을 불러오기 위한 import (ex. import { funcA } from 'A.js';)
  • babel-preset-react-app : CRA에서 사용된 Babel 프리셋으로, 해당 프리셋 모듈안에 있는 create.js에 설정된 환경(개발, 프로덕션) 별 프리셋플러그인 설정을 내보냄

Webpack 관련

  • webpack : Javascript 기반의 모듈 번들러로, React App의 여러 모듈들을 의존관계끼리 엮어 하나 이상의 번들 정적 에셋으로 번들링하는 툴
  • webpack-dev-server: 웹팩과 함께 사용하는, 라이브 리로딩을 제공하는 개발 서버로, 내부적으로 webpack-dev-middleware를 사용해 웹팩 에셋에 빠르게 인-메모리 접근
  • webpack-manifest-plugin: 에셋 매니페스트를 생성하기 위한 웹팩 플러그인으로, 생성된 매니페스트는 아래와 같이 소스 파일 이름 - 대응되는 빌드 output 파일 간의 매핑들을 포함함 (이를 통해 추후 새롭게 번들링된 파일의 파일명빠르게 파악해 사용 가능)
    {
      "dist/batman.js": "dist/batman.1234567890.js",
      "dist/joker.js": "dist/joker.0987654321.js"
    }
    
  • workbox-webpack-plugin: PWA(프로그레시브 웹 앱)을 위한 라이브러리 모음인 Workbox의 웹팩용 플러그인으로, 서비스 워커 생성(GenerateSW)사전 캐싱(precache)할 에셋 목록(매니페스트)을 생성해 서비스 워커 파일에 주입(InjectManifest) 하는 기능을 제공 #
  • terser-webpack-plugin: 변수 이름 축소, 공백 및 주석 제거, 미사용 코드 제거 등을 통해 코드를 압축(최소화)하는 Terser 의 웹팩 플러그인
    ...
    minimizer: [
        // This is only used in production mode
        new TerserPlugin({
          terserOptions: {
            parse: {
              // We want terser to parse ecma 8 code. However, we don't want it
              // to apply any minification steps that turns valid ecma 5 code
              // into invalid ecma 5 code. This is why the 'compress' and 'output'
              // sections only apply transformations that are ecma 5 safe
              // https://github.com/facebook/create-react-app/pull/4234
              ecma: 8,
            },
            compress: {
              ecma: 5,
              warnings: false,
    		  // Terser가 uglify-es의 포크이기 때문에, Uglify에서 발생했던
    		  // '특정 라이브러리 사용시 비교 연산자 처리 버그'가 있기 때문에
    		  // 비교 연산자를 압축하는 것을 비활성화 함
    		  // https://github.com/mishoo/UglifyJS2/issues/2011
              comparisons: false,
    		  // 작은 함수들을 인라인 함수로 변환해 함수 오버헤드를 줄이는 옵션으로
    		  // 값이 2일경우 적절한 인라인 레벨을 Terser가 선택하도록 함
    		  // 정확히는, inline functions with arguments, 즉 
    		  // 아래 두 가지 버그 이슈때문에 2로 명시
              // https://github.com/facebook/create-react-app/issues/5250
              // https://github.com/terser-js/terser/issues/120
              inline: 2,
            },
            mangle: {
    		  // 변수 및 함수 이름 난독화 관련 옵션으로, 
    		  // safari10 이란 Safari 10 브라우저의 버그를 회피하기 위한 설정
              safari10: true,
            },
    		// 개발 툴에서 프로파일링 할 수 있도록, 클래스 이름과 함수 명을 난독화할지 설정하는 변수
            keep_classnames: isEnvProductionProfile,
            keep_fnames: isEnvProductionProfile,
            output: {
    		  // 압축된 코드의 출력 형태와 관한 옵션
    		  // 최종적으로 압축된 코드는 ES5 버전 호환성을 지님
              ecma: 5,
    		  // 모든 주석을 제거하도록 함
              comments: false,
    		  // 문자열 및 정규 표현식에 존재하는 유니코드 문자를 ASCII 이스케이프 시퀀스(\uXXXX와 같은 형태. ex) `H` => `\u0048`)로 변경
              // 이모티콘 또는 특정 정규 표현식이 default로는 제대로 압축되지 않기 때문
              // https://github.com/facebook/create-react-app/issues/2488
              ascii_only: true,
            },
          },
        }),
        // This is only used in production mode
        new CssMinimizerPlugin(),
      ],
    },
    
    • eject된 webpack.config.js 설정에 따르면, 프로덕션 환경에서 이러한 코드 압축이 수행되며 ES8(ECMAScript 2017) 문법까지 이해하고 파싱하되 코드 압축(compress)출력(output)ES5 호환성을 유지하도록 코드를 minimize하려 함
    • 이외에도 비교 연산자를 압축하지 않는다거나, 클래스 이름 및 함수 명을 난독화 할지 등을 설정하고 있음
  • css-minimizer-webpack-plugin: cssnano를 사용해 CSS 파일들을 최적화 및 최소화하는 웹팩 플러그인으로, 캐싱병렬 처리를 지원하는 것이 특징임
  • @pmmmwh/react-refresh-webpack-plugin: React Component에 대해 핫 리로딩이라고도 부르는 빠른 새로고침을 가능하게 해주는 웹팩 플러그인
    • react-dom, react-refresh 등의 모듈에 의존하므로 선행 설치가 필요함
  • case-sensitive-paths-webpack-plugin: 모든 필수 모듈의 전체 경로디스크 상 실제 경로정확히 일치하도록 강제하는 웹팩 플러그인
    • 이를 통해 대소문자에 둔감(case-insensitive)한 환경(ex. OS X(macOS))의 개발자가 대소문자 구분 규칙을 지키지 않아 다른 개발자나 빌드 환경에서 충돌이 발생하는 문제를 방지
  • eslint-webpack-plugin: 웹팩 빌드 과정에서 코드의 문제를 발견하고 고치기 위해 ESlint를 사용하는 플러그인
    • webpack.config.js에서는 react-dev-utils/eslintFormatter 포매터와 eslint-config-react-app/base 설정을 사용해 린팅

로더 #

  • 웹팩이 처리할 수 없는 Javascript 파일 이외의 파일들 을 변환할 수 있도록 도와주는 라이브러리로, 파일을 import하거나 로드할 때 사전처리를 수행할 수 있음
    • 로더체이닝 될 수 있으며, 체인의 각 로더는 각각 (앞선 로더에 의해) 처리된 리소스를 전달 받아 변환을 적용함
    • 최종적으로, Webpack은 이러한 체인의 마지막 로더로 부터 Javascript 파일을 넘겨 받기를 기대함
  • 로더는 webpack.config.jsmodule.rules 배열에서 지정할 수 있으며, 선언된 역순으로 로더가 적용됨
    • 예를 들어 module: { rules: ["style-loader", "css-loader", "sass-loader"]} 라고 선언이 되어있다면, sass-loader -> css-loader -> style-loader 순으로 작업이 이뤄짐
  • babel-loader : 웹팩을 위한 Babel Module 로더
  • @svgr/webpack: SVGR 을 위한 웹팩 로더
    // SVGR 예시 코드
    import Logo from 'assets/logo.svg'; // SVG 파일을 React Component 처럼 import
    
    const App = () => {
      return (
        <div>
          <Logo width="100" className="mx-auto" />
        </div>
      );
    };
    
    • SVGR이란, SVGReact Component로 변환해주는 툴로, React Component에서 SVG 파일들을 컴포넌트 쓰듯 import 해 사용할 수 있게 해주는 툴
  • css-loader: 사용된 CSS문자열로 변환해주는 로더로, @importurl()import / require()와 동일하게 해석resolve 해줌
    // 예시 1. url() -> require()
    url(image.png) => require('./image.png')
    // 예시 2. @import -> require()
    @import 'style.css' => require('./style.css')
    // 예시 3. @import url() -> require()
    @import url(style.css) => require('./style.css')
    
    // 변환 전 CSS
    h1 {
    	color: #FFFFFF;
    }
    
    body {
    	background-color: #272727;
    }
    
    // 문자열로 변환된 CSS
    ... [n.id, "h1 {\n	color: #FFFFFF;\n}\nbody {\n	background-color: #272727;\n}"]
    
    • 또한, CSS Module을 지원해 CSS 클래스 이름이 전역적으로 충돌하는 것을 방지하게 해줌
  • style-loader: css-loader함께 쓰길 권장하는 로더로, CSSDOM에 주입하는 로더 (정확히는, CSS를 DOM에 주입하는 Javascript 코드로 변환하는 역할)
    • 옵션의 injectType을 통해 어떻게 styleDOM에 주입될 지 지정할 수 있음
      • 하나 또는 여러 개의 <style> 태그로 주입할 지(singletonStyleTag/styleTag), <link> 태그를 통해 css 파일을 참조하도록 할 지(linkTag) 등의 옵션이 있음

CRA 프로젝트 설정 에서는 개발 환경일 경우 style-loader를, 프로덕션 빌드에서는 MiniCssExtractPlugin를 사용

CSS / SASS 관련

  • browserslist: Babel, Autoprefixer 등 여러 프론트엔드 툴에서 동일한 타깃 브라우저 및 Node.js 런타임 버전을 공유하기 위한 Configuration
    • 이를 통해 불필요한 Polyfil, 벤더 프리픽스 등을 줄일 수 있으며, 프론트엔드 툴 간 일관성이 보장됨
    • CRA 프로젝트 설정에서는 프로덕션 빌드의 경우 0.2% 이상의 점유율을 지녔으며(>0.2%) 24개월 내 업데이트가 없는 브라우저를 제외하고(not dead) 오페라 미니를 제외한(not op_mini all), 개발 환경에서는 가장 최신 버전의 크롬/파이어폭스/사파리 를 지원 하도록 설정됨
  • mini-css-extract-plugin : CSS를 포함하고 있는 JS 파일 별로 CSS를 추출JS 파일 당 CSS 파일을 생성 하는 웹팩 플러그인으로, css-loader와 함께 사용하기를 권장함
    • 다만, 웹팩 엔트리 포인트 또는 initial chunk에서 import 하고 있는 CSS의 경우, (이 플러그인이) 페이지에서 해당 CSS들을 로드하도록 하지 않음
      • 엔트리 포인트: webpack.config.js에서 entry로 설정된 파일
      • initial chunk: 엔트리 포인트의 메인 청크를 의미하며, 엔트리 포인트명시된 모든 모듈 및 의존성을 포함하고 있는 청크
    • 즉, 엔트리 포인트에 대응되는 CSS 파일의 경우, 자동으로 <link> 태그를 생성하거나 / <link> 태그를 포함한 index.html을 만들기 위해 html-webpack-plugin를 사용해야 함
      • html-webpack-plugin: 웹팩 번들 서빙을 위한 HTML 파일 생성을 간소화 시켜주는 웹팩 플러그인
  • tailwindcss
  • sass-loader
  • postcss
  • postcss-flexbugs-fixes
  • postcss-loader
  • postcss-normalize
  • postcss-preset-env

ESLint 관련

  • eslint
  • eslint-config-react-app

테스트 관련

  • jest
  • jest-resolve
  • jest-watch-typeahead

기타 유틸

  • bfj
  • camelcase
  • dotenv
  • dotenv-expand
  • file-loader
  • fs-extra
  • identity-obj-proxy
  • prompts
  • react-app-polyfill
  • react-dev-utils
  • react-refresh
  • resolve
  • resolve-url-loader
  • semver
  • source-map-loader

당장 필요없는 것 덜어내기