Skip to main content

[Webpack/Vite 파헤치기] 2. Create React App 내부의 의존성과 환경 설정 꺼내기

서론

앞선 게시물에 이어, 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 관련 종속성, Webpack 관련 종속성(Webpack Loader), CSS/SASS 관련 종속성, ESLint 관련 종속성, 테스트 관련 종속성, 기타 유틸 로 이뤄져 있음

Babel 관련

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이란?

  • 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 로더

    • .js, .jsx, .ts, .tsx 와 같은 파일들에 대해 폴리필, TS 및 React JSX 트랜스파일링 등의 작업을 수행하도록 연결
  • @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 내부에서 배포하는 패키지로, 모듈 경로를 Resolve 하는데 사용되는 AsyncResolver, SyncResolver와 같은 Resolver를 배포
    • AsyncResolver - SyncResolver는 이름 그대로 모듈이름을 동기적으로(resolveStubModuleName : string) 또는 비동기적으로(resolveStubModuleNameAsync : Promise) Resolve 할 것인지 차이
  • jest-watch-typeahead: 파일 이름 또는 테스트 이름으로 테스트들을 필터링해 실행할 수 있는 플러그인

기타 유틸

  • bfj: Big-Friendly JSON의 약자로, 대형 JSON 데이터 셋비동기 스트리밍 함수를 제공
    • 만약 큰 JSON 문자열파싱하거나, 대규모 데이터 셋문자열로 변환할 경우 이벤트 루프를 독점하고, OOM(메모리 부족) 예외를 발생시킬 수 있음
    • 이때, BFJ비동기 함수, 그리고 사전-할당된 고정-길이 배열을 사용해 이런 문제를 완화하려고 함
    • CRA 프로젝트 설정에서는 빌드 시 발생하는 stat(warning, error 등)들을 기록한 bundle-stats.json 파일을 작성하기 위해 사용
  • camelcase: 대시(-)/점(.)/밑줄(_)/공백( )으로 구분된 문자열을 camelCase 또는 PascalCase로 변환해주는 함수를 제공
    • camelCasePascalCase 모두, 단어들을 합칠때 공백없이 각 단어의 첫 글자를 대문자로 표기한다는 공통점이 있지만 camelCase첫 번째 단어의 첫 글자를 소문자로, PascalCase첫 번째 단어를 포함한 모든 단어의 첫 글자를 대문자로 표기한다는 차이가 있음
    • CRA 프로젝트 설정에서는 커스텀 Jest 트랜스포머(fileTransform.js)에서 SVG 파일React 컴포넌트처럼 가져올 때, SVG 파일 이름을 PascalCase로 처리하기 위해 사용
  • dotenv: .env 파일로부터 환경 변수들을 불러와, node.jsprocess.env 객체에 주입하는 패키지
    • .env 파일에는 키=값으로 이루어진 변수들을 두고 사용
    • 2년 전부터 dotenv"암호화 된" .env 파일 을 지원하고, 크로스-플랫폼 일관성 을 지원하는 dotenvx를 사용하길 권고하고 있음
  • dotenv-expand: dotenv변수 확장(variable expansion) 기능을 추가한 패키지
    • 변수 확장: 한 변수에서 다른 변수를 ${이름} 구문을 통해 참조할 수 있는 기능
      	HOST_URL=http://localhost:3000 
      	API_URL=${HOST_URL}/api
      
    • CRA 프로젝트 설정에서는 env.js에서 환경 변수들을 설정할 때, .env파일들을 우선순위가 높은대로 배열에 담은 뒤 순회하며 dotenv 를 통해 환경변수들을 불러온 뒤, 그 사이에 존재하는 변수 확장을 처리하기 위해 dotenv-expand로 한 번 더 처리하는 형태로 사용
  • fs-extra: node.jsfs 모듈에 포함되어 있지 않은 파일 시스템 메서드를 추가하고, fs 메서드들에 대한 Promise 지원을 추가한 패키지
    • fs 모듈의 모든 기능을 포함한채 확장하기 때문에 fs-extra만 가져와 사용하면 됨
    • 또한, 모든 fs 메서드들이 Promise를 지원하도록 했기 때문에, 별도의 callback을 넘기지 않을 경우 Promise를 반환함
    • ESM(ES Module)도 지원하나(fs-extra/esm), fs 메서드들이 포함되어 있지 않으므로 별도로 import 해주어야 함
    • CRA 프로젝트 설정에서는 애플리케이션을 build할 때 사용됨
      // 1. Yarn 패키지 매니저가 사용되는지 확인하는데 사용
      const useYarn = fs.existsSync(paths.yarnLockFile);
      
      // 2. 빌드 디렉토리 내 파일들을 지우는데 사용
      fs.emptyDirSync(paths.appBuild);
      
      // 3. `Public` 폴더 내 정적 에셋들을 빌드 디렉토리로 복사하는데에 사용 
      fs.copySync(paths.appPublic, paths.appBuild, {
      dereference: true,
      filter: file => file !== paths.appHtml,
      });
      
  • identity-obj-proxy: ES6 Proxy를 이용한 항등(Identity) 객체를 제공하는 패키지로, CSS 모듈과 같이 Webpack이 처리해주는 import 동작을 모킹하는데 유용함
    • Proxy: 다른 객체에 대한 프록시를 생성하여, 해당 객체의 기본적인 작업가로채고, 재정의할 수 있도록 하는 ES6 문법
      • Proxy(obj, handler): Proxy 생성자는 프록시할 다른 객체(obj)다른 객체의 어떤 작업을 가로채고 해당 작업을 어떻게 재정의할 지 정의된 객체(hanlder) 두 가지 매개변수를 받음
        • 정의할 수 있는 handler 함수로는 apply(), construct(), get(), set() 등이 있으며, 아래 idObj에서는 객체 프로퍼티에 접근하는 메서드get()을 재정의한 handler function을 정의하고 있음
      • identity-obj-proxy에서는 아래와 같이, 접근하려는 key 이름을 그대로 반환하는 Proxy 객체를 제공함
        idObj = new Proxy({}, {
          // 객체 내부 메서드(Object internal methods)인 get 호출을 가로채고 대신 응답하는 handler function
          get: function getter(target, key) {
            if (key === '__esModule') {
              return false;
            }
            return key;
          }
        });
        
    • 항등(Identity) 객체: 모든 원소를 자기 자신과 같은 값을 대응시키는 함수인 항등 함수(ex) f(x) = x)처럼, 모든 key에 대해 obj[key] = 'key' (또는 obj.key = 'key')인 객체
    // 예시 코드
    
    // jest.config.js
    // ...
    module.exports = {  
    	moduleNameMapper: {  
    	'\\.jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':  
    		'<rootDir>/__mocks__/fileMock.js',   
    	// CSS 또는 LESS(Leaner Style Sheets) 정적 에셋 모듈 경로에 대해 identity-obj-proxy의 프록시 객체를 대신 사용하도록 설정
    	// 즉, 프로젝트 내 사용되는 모든 CSS Module에 대해 className을 `identity-obj-proxy`에서 제공하는 프록시 객체에서 lookup 하도록 설정
    	'\\.(css|less)$': 'identity-obj-proxy',
    	},  
    };
    
    // SomeComponent.js
    import React, { Component } from 'react';
    
    // jest와 같은 프레임워크를 통해 컴포넌트를 테스트 할 땐 이미지, CSS와 같은 정적 에셋들이 특별히 유용하진 않음
    // 따라서, 이들을 실제처럼 `css-loader`와 같은 툴을 통해 해싱된 클래스 명을 주입하기 보다, 모킹된 프록시를 만들어 className이 일치하는지만 확인하도록 하는 것이 더 안전하고 편리
    // 따라서 `identity-obj-proxy`와 같은 모듈이 제공하는 `항등 객체`에서 className을 lookup해 확인하도록 설정
    import styles from './App.css'; // CSS Module import
    
    export default class App extends Component {
      render() {
        return (
          <div className={styles.root}>
            <h1 className={styles.hello}>Hello, world!</h1>
          </div>
        );
      }
    }
    
  • prompts: CLI 프롬프트를 쉽게 만들어주는 패키지
    • jest-cli, react-dev-utils에서 의존하는 패키지
  • react-app-polyfill: Create-React-App 프로젝트에서 일반적으로 사용된 Javascript 기능최소 요구 사항에 대한 Polyfill을 포함하는 패키지
    • 프로젝트에서 지원하고자 하는 최소 버전의 진입점import하면 Create React App 프로젝트를 이용하기 위한 최소 언어 기능을 포함하도록 할 수 있음
    // 아래와 같은 import문을 프로젝트 진입점('src/index.js')의 첫 번째 줄로 기입해야 함
    
    // 최소 IE 9버전 이상(IE9, IE10, IE11, ...)을 지원하려는 경우
    import 'react-app-polyfill/ie9';
    
    // 최소 IE 11버전 이상을 지원하려는 경우
    import 'react-app-polyfill/ie11';
    
    • 또한, stable한 기능이지만 타겟 브라우저에서 이를 지원하지 않는 경우 이를 제공하는 Polyfill('react-app-polyfill/stable')을 사용할 수 있음 (Create-React-App을 사용하고 있는 경우, 사용자가 정의한 browserlist를 자동적으로 불러와, 타겟 브라우저에서 필요한 폴리필만 가져옴)
  • react-dev-utils: Create-React-App에서 사용되는 몇가지 유틸리티들을 포함하는 패키지
    • react-dev-utils단일 엔트리 포인트를 갖고 있지 않으며, 각각의 모듈을 직접 import 해야 하는 형태로 작성되어 있음
    • react-dev-utils를 구성하는 주요 모듈 중, CRA 프로젝트에서 사용된 모듈은 다음과 같음
      • InterpolateHtmlPlugin: html-webpack-plugin을 사용해 index.html사용자 정의 변수삽입할 수 있도록 돕는 웹팩 플러그인
      • InlineChunkHtmlPlugin: 마찬가지로 html-webpack-plugin을 사용해 index.html인라인 스크립트 청크를 삽입하는 웹팩 플러그인
        • 즉, <script src="..."> 대신 <script>태그 내부에 직접 코드를 삽입
      • ModuleScopePlugin: 애플리케이션의 소스 디렉토리에서 상대 경로로 가져온(relative import) 파일들을 디렉토리 외부에서 접근하지 못하도록 보장하는 웹팩 플러그인
      • getCSSModuleLocalIdent: index.module.css라는 이름의 CSS 파일에 대해 파일 이름 또는 폴더 이름을 사용한 CSS 모듈 클래스 이름을 생성해주는 style-loader 모듈
      • ModuleNotFoundPlugin: Webpack에서 발생하는, 모듈을 찾을 수 없다는 에러를 알기 쉽게 꾸며주는 웹팩 플러그인
      • ForkTsCheckerWebpackPlugin: 타입스크립트 타입 검사를 별도의 프로세스에서 실행하는 웹팩 플러그인
      • eslintFormatter: Create React App 콘솔 출력과의 통합을 위한 커스텀 ESLint 포매터
      • checkRequiredFiles: 필수 파일 목록을 받아 포함되지 않은 파일들chalk를 사용해 터미널에 출력하는 함수
      • formatWebpackMessages: webpack의 stats 객체가 갖고있는 경고 및 에러 메시지추출하고 꾸며주는 유틸리티 함수
      • printHostingInstructions: 패키지 경로, public URL 등을 받아, 프로젝트 빌드 후 호스팅 지침을 출력해주는 유틸리티 함수
      • FileSizeReporter: 빌드 전 JS 및 CSS 에셋 파일 크기를 기록해두고, 빌드 후 이들의 크기 비교를 수행하고 출력하는 유틸리티 함수 모음
      • printBuildError: 잘 알려진 빌드 에러들을 꾸며주는 유틸리티 함수
      • evalSourceMapMiddleware: /__get-internal-source로 시작하는 요청에서 웹팩 내부 URL(webpack-internal:///<module-id>)의 module id를 추출해 <source-text><sourceMappingURL><sourceURL> (모듈 원본 소스코드 + 소스맵 URL(base64 인코딩된 소스맵 정보) + 소스 URL )를 반환하는 웹팩 내부 미들웨어 생성 함수
      • noopServiceWorkerMiddleware: ${servedPath}/service-worker.js라는, 이전의 Service Worker 설정을 초기화하는 파일을 제공하는 Express 미들웨어
      • ignoredFiles: 소스 디렉토리 외부의 node_modules파일 감시에서 무시하기 위해, 정규표현식을 활용해 파일 경로 패턴을 생성하는 함수
        // webpackDevServer.config.js
              watch: {
                // Reportedly, this avoids CPU overload on some systems.
                // https://github.com/facebook/create-react-app/issues/293
                // src/node_modules is not ignored to support absolute imports
                // https://github.com/facebook/create-react-app/issues/1065
                ignored: ignoredFiles(paths.appSrc),
              },
        
      • redirectServedPathMiddleware: req.urlservedPath로 시작하지 않을 경우 ${servedPath}/${req.path}로 리다이렉션 시키는 Express 미들웨어
      • clearConsole: 콘솔 내용을 지우는 유틸리티 함수
      • WebpackDevServerUtils: WebpackDevServer를 위한 웹팩 컴파일러 인스턴스를 생성하거나, 대체 포트를 제공하거나, package.jsonproxy세팅을 기반으로 WebpackDevServer 프록시 설정 객체를 생성하는 등의 작업을 수행하는 유틸리티 함수 모음
      • openBrowser: 주어진 URL과 함께 브라우저를 여는 유틸리티 함수
  • react-refresh: Fast Refresh라고 부르는, “실행중인 React 애플리케이션에서 컴포넌트를 상태값의 손실 없이 편집할 수 있도록 해주는 기능"을 번들러에 통합시키기 위한 패키지
  • resolve: node.jsrequire.resolve() 알고리즘을 구현하여, 비동기/동기적으로 require.resolve()를 호출할 수 있도록 해주는 패키지
    • require.resolve(): require()를 통해 모듈을 탐색할 때, 주어진 경로를 해석하는 함수
      • 대략적인 pseudo algorithm은 문서참고
  • semver: npm을 위한 시멘틱 버전 관리(Semantic Versioning) 도구.
    • Semantic Versioning 2.0.0 | Semantic Versioning에 따르면, 의존성이 많은 시스템에서 의존성 지정이 너무 엄격해 발생할 수 있는 버전 락(Version Lock), 또는 의존성이 너무 느슨하게 지정되어 발생할 수 있는 버전 난잡성(Version Promiscuity)를 방지하기 위해서 시멘틱 버전 관리 사양을 제안함
    • 시멘틱 버저닝에서는, X.Y.Z와 같은 체계의 형태로 버전 번호를 관리하는데, 이때 각 부분은 다음을 의미
      • X: 메이저 업데이트. 하위 호환성이 없는 API변경 사항
      • Y: 마이너 업데이트. 하위 호환성이 있는(Backward Compatible) API추가/변경 사항
      • Z: 패치. API에 영향을 주지않는 버그 수정(Bug Fixes)
    • 이러한 시멘틱 버저닝 관리 체계에 있어, 유용한 여러 유틸리티 함수를 제공하는 것이 semver
      const semver = require('semver')
      
      semver.valid('1.2.3') // '1.2.3'
      semver.valid('a.b.c') // null
      semver.clean('  =v1.2.3   ') // '1.2.3'
      semver.satisfies('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') // 첫 번째 인자가 두 번째 인자로 주어진 버전 조건을 만족하는가? true
      semver.gt('1.2.3', '9.8.7') // 첫 번째 인자가 두 번째 인자보다 높은 버전인가? false
      semver.lt('1.2.3', '9.8.7') // 첫 번째 인자가 두 번째 인자보다 낮은 버전인가? true
      

Ejecting 이후 콘솔에 출력된 메세지

// 의존성용 config 파일들 및 스크립트 파일 생성
  Adding \config\env.js to the project
  Adding \config\getHttpsConfig.js to the project
  Adding \config\modules.js to the project
  Adding \config\paths.js to the project
  Adding \config\webpack.config.js to the project
  Adding \config\webpackDevServer.config.js to the project
  Adding \config\jest\babelTransform.js to the project
  Adding \config\jest\cssTransform.js to the project
  Adding \config\jest\fileTransform.js to the project
  Adding \scripts\build.js to the project
  Adding \scripts\start.js to the project
  Adding \scripts\test.js to the project
  Adding \config\webpack\persistentCache\createEnvironmentHash.js to the project

// 감춰져있던 의존성들 추가
Updating the dependencies
  Removing react-scripts from dependencies
  Adding @babel/core to dependencies
  Adding @pmmmwh/react-refresh-webpack-plugin to dependencies
  Adding @svgr/webpack to dependencies
  Adding babel-jest to dependencies
  Adding babel-loader to dependencies
  Adding babel-plugin-named-asset-import to dependencies
  Adding babel-preset-react-app to dependencies
  Adding bfj to dependencies
  Adding browserslist to dependencies
  Adding camelcase to dependencies
  Adding case-sensitive-paths-webpack-plugin to dependencies
  Adding css-loader to dependencies
  Adding css-minimizer-webpack-plugin to dependencies
  Adding dotenv to dependencies
  Adding dotenv-expand to dependencies
  Adding eslint to dependencies
  Adding eslint-config-react-app to dependencies
  Adding eslint-webpack-plugin to dependencies
  Adding file-loader to dependencies
  Adding fs-extra to dependencies
  Adding html-webpack-plugin to dependencies
  Adding identity-obj-proxy to dependencies
  Adding jest to dependencies
  Adding jest-resolve to dependencies
  Adding jest-watch-typeahead to dependencies
  Adding mini-css-extract-plugin to dependencies
  Adding postcss to dependencies
  Adding postcss-flexbugs-fixes to dependencies
  Adding postcss-loader to dependencies
  Adding postcss-normalize to dependencies
  Adding postcss-preset-env to dependencies
  Adding prompts to dependencies
  Adding react-app-polyfill to dependencies
  Adding react-dev-utils to dependencies
  Adding react-refresh to dependencies
  Adding resolve to dependencies
  Adding resolve-url-loader to dependencies
  Adding sass-loader to dependencies
  Adding semver to dependencies
  Adding source-map-loader to dependencies
  Adding style-loader to dependencies
  Adding tailwindcss to dependencies
  Adding terser-webpack-plugin to dependencies
  Adding webpack to dependencies
  Adding webpack-dev-server to dependencies
  Adding webpack-manifest-plugin to dependencies
  Adding workbox-webpack-plugin to dependencies

Updating the scripts

Configuring package.json
  Adding Jest configuration
  Adding Babel preset

참고

  • 각 의존성들의 Github Repository 및 공식 문서