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-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를 사용

  • sass-loader: SASS/SCSS 파일을 불러와, CSS컴파일하는 로더
    • css-loader, style-loader와 체이닝해 DOM에 즉각적으로 스타일들을 반영하거나, mini-css-extract-plugin과 체이닝해 개별 CSS 파일로 추출할 수 있음
      • 이때, 체인에서 sass-loader가 이들보다 앞에(즉, 로더 배열의 후순에) 위치해야 함
  • postcss-loader: 프로젝트에서 사용된 CSS 파일을 불러와, PostCSS의 여러 처리(CSS 린팅, 최신 CSS 문법의 폴리필 등)들을 수행하도록 하는 로더
    • CRA 프로젝트 설정에서는 tailwindcss, postcss-preset-env, postcss-normalize 플러그인을 사용
  • file-loader: Javascript 코드 중 파일을 import / require()하는 구문에 대해, 해당 파일을 지정된 디렉토리(output directory)로 이동시킨 뒤 public URL을 생성하는 로더
    • public URL: 애플리케이션 내 모든 asset들이 위치할 기본 경로Public Path와 해당 자원의 위치를 결합한 URL
    	// 변환 전
    	import png from './image.png';
    
    	// 변환 후
    	const png = "dirname/0dcbbaa701328ae351f.png"; // 'dirname/[contenthash].[ext]' 포맷
    
  • resolve-url-loader: 분산된 SCSS 파일과, 각 SCSS파일과 관련된 에셋들(이미지, 폰트 등)을 같은 곳에 위치시키도록 해주는 로더
    • 여러 곳에 SCSS 파일을 분산해두었으며, 그와 관련된 에셋들을 같은 곳에 위치시켜 사용하고, 에셋에 대한 접근완전한 상대경로(현재 파일을 기준으로한 상대 경로, url(./filename.ext) 또는 url(filename.ext))로 접근하는 경우 적합
    • 웹팩url() 구문에서 완전한 상대경로가 사용됐을 경우 해당 에셋루트 SCSS 파일같은 디렉토리 에 존재한다고 가정하기 때문에 분산 저장된 SCSS 및 에셋 들을 제대로 처리하지 못함 distributed-css
    • resolve-url-loader는 이러한 URL들을 재작성하여 제대로 동작하도록 도와줌
    • 참고1) resolve-url-loader보다 앞서 처리되는 로더가 있는 경우, 해당 로더들은 소스 맵을 제공해야 함
      • 그래야 resolve-url-loader앞선 로더들이 전처리한 CSS 코드속의 상대 경로올바르게 파악할 수 있기 때문
  • source-map-loader: 소스 코드 파일들로부터 소스맵추출하는 로더
    • 소스맵: 원본 소스코드트랜스파일된 소스 코드 간의 매핑 정보가 선언된 파일
      • 압축, 난독화 등의 처리를 거친 코드원본 코드 간의 관계를 알려주어, 트랜스파일링된 코드디버깅을 도움
    • 애플리케이션 내(node_modules 폴더를 포함한) 모든 Javascript 엔트리 로 부터 이미 존재하는 소스맵을 추출하며, 이는 인라인 소스맵 뿐만 아니라 URL로 링크된 소스맵도 포함
      • 이러한 처리는 자체 소스맵을 갖고있는 라이브러리의 소스맵웹팩 번들의 소스맵으로 추출처리되지 않을 경우, 브라우저소스맵 데이터잘못 해석하는 일을 방지해줌

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 파일 생성을 간소화 시켜주는 웹팩 플러그인

      If you have any CSS assets in webpack’s output (for example, CSS extracted with the mini-css-extract-plugin) then these will be included with <link> tags in the HTML head. ( 만약 Webpack 출력이 CSS 에셋을 포함할 경우, HTML <head><link> 태그로 포함될 것입니다.) 출처: jantimon/html-webpack-plugin: Simplifies creation of HTML files to serve your webpack bundles

  • tailwindcss: 유틸리티-우선(utility-first) CSS 프레임워크로, 프레임워크에서 지정한 클래스 명을 사용하면 CSS 유틸리티가 적용됨
  • postcss: Javascript 플러그인을 사용해 CSS 린팅, 변수믹스인 지원, 최신 CSS 문법의 트랜스파일 등의 기능을 제공하는 변환기
    • 내부적으로 CSS -> Parser -> [Plugin 1, Plugin 2, ... ] -> Stringifier -> New CSS로 이어지는 구조를 갖고 있음
      • CSS를 TokenizerParser를 통해 분해AST화 한 뒤, Plugin 들을 적용시키고 나온 AST순수 CSS 문자열로 변환함
    • 널리 사용되고 있는 autoprefixer가 대표적인 PostCSS 플러그인임
  • postcss-flexbugs-fixes : flowbox 이슈크로스-브라우저 이슈수정하기 위한 PostCSS 플러그인
  • postcss-normalize: browserlist 설정에 맞춰 normalize.css 라이브러리 또는 sanitize.css 라이브러리를 사용할 수 있도록 해주는 툴
    • normalize.css : HTML 요소에 대해 크로스-브라우저 간 일관된 스타일을 제공하는 CSS 라이브러리
      • nomarlize.css의 내용을 살펴보면, 중첩 리스트(dl, ol, ul)의 margin 없애기(line 36), Firefox에서의 box-sizing 추가border 색상을 상속하도록 정정(line 46) 하는 등의 내용을 포함함
    • sanitize.css : normalize.css의 모든 요소들이 포함되어 있으며, 나아가 유용한 기본 설정(useful defaults)을 포함하는 CSS 라이브러리
      • sanitize.css의 내용을 살펴보면, 커서불명확한 인터페이스에 힌트를 주기 위해서만 변하도록 하거나(line 37), background-repeat이 기본적으로 반복되지 않도록 하는(line 13) 등의 내용을 포함함
  • postcss-preset-env: 최신 CSS 문법브라우저가 이해할 수 있도록 변환하고, 타겟 브라우저 및 런타임에 맞춰 필요한 폴리필결정하는 PostCSS 플러그인
    • MDNCan I Use로부터 버전 별 지원 기능 정보들을 가져와 browserlist를 기반으로 기능의 변환이 필요한지 판단하며, 내부적으로 autoprefixer를 포함해 브라우저 지원 목록에 따라 필요할때만 접두사를 적용하도록 함
    • CRA 프로젝트 설정에서는 autoprefixer: { flexbox: 'no-2009' }, stage: 3라고 옵션을 지정하여 최신IE 버전flexbox 문법에만 접두사를 추가하도록 하며, 또한 stage 3에 해당하는 CSS 기능들을 변환하도록 설정

ESLint 관련

  • eslint: Javascript 코드에서 발견된 패턴식별보고하는 툴
    • 여기서 말하는 “패턴"으로는 문법 오류, 버그를 일으킬 수 있는 사용되지 않는 변수/접근 불가능한 코드 등의 요소, 정해진 코딩 컨벤션을 위반하는 코드 등이 있음
    • Espree를 사용해 Javascript 코드를 파싱하며, 패턴을 확인하기 위해 AST를 사용함
    • 사람들이 종종 대체제로 오인하는 Prettier와는 다른 작업을 수행하는 툴임: Linter(ESLint)와 Code Formatter(Prettier)
      • Linter는 코드에서 발견되는 잠재적 오류, 안티 패턴 등을 식별하고 경고 하는 도구이고, Code Formatter들여쓰기(indent), 줄바꿈(line break) 등의 요소들을 일관되도록 하여 코드 가독성 을 높이는 도구임
  • eslint-config-react-app: CRA에서 사용되는 ESLint 설정을 포함하고 있는 패키지

테스트 관련

  • jest: 자바스크립트 테스팅 프레임워크로, 테스트를 찾아 실행하는 테스트 러너 뿐만 아니라 Test Matcher(expect()와 같은 Assertion)그리고 Test Mock(jest.spyOn, jest.mock 등) 프레임워크를 제공
    • CRA 프로젝트 설정에서는 <rootDir>/src 내 모든 js/jsx/ts/tsx 파일 중 __test__폴더 내 모든 테스트 파일 또는 *.spec.{확장자}/*.test.{확장자} 파일 에 대해 테스트를 수행하도록 설정되어 있음
  • babel-jest : 코드 전처리(transform)를 위해 Babel을 사용하도록 하는 Jest의 플러그인
    • Javascript/JSX 파일 테스트 전, 전처리 수행
    • CRA 프로젝트 설정에서는 babel-preset-react-app 프리셋을 사용하고, 해당 프리셋에서 runtimeclassic(react 17 미만) 또는 automatic(_jsx(react/jsx-runtime)을 사용하는 환경)으로 설정한 트랜스포머를 생성해 사용하는데에 쓰임
  • 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

당장 필요없는 것 덜어내기