[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
fornamed asset imports
in Create React App- JS/CSS를 제외한
정적 자원
들을Named Import
형태로 가져올 수 있도록하는 플러그인Named Import
: default export가 아닌,named Export
된 요소들을 불러오기 위한 import (ex.import { funcA } from 'A.js';
)
- JS/CSS를 제외한
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
하려 함 - 이외에도 비교 연산자를 압축하지 않는다거나, 클래스 이름 및 함수 명을 난독화 할지 등을 설정하고 있음
- eject된
css-minimizer-webpack-plugin
: cssnano를 사용해CSS 파일들을 최적화 및 최소화
하는 웹팩 플러그인으로,캐싱
및병렬 처리
를 지원하는 것이 특징임cssnano
란,PostCSS
생태계 기반의 CSS 압축 툴로주석/공백/줄 바꿈 제거
,헥스코드 축약
,단위 제거
등의 압축을 수행- 테스트 해볼 수 있는 사이트: Minify CSS Online. CSS Minification tool powered by CSSNANO. | Minify CSS Online
@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.js
의module.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
이란,SVG
를React Component
로 변환해주는 툴로,React Component
에서SVG 파일
들을 컴포넌트 쓰듯 import 해 사용할 수 있게 해주는 툴
css-loader
: 사용된CSS
를문자열
로 변환해주는 로더로,@import
와url()
을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
와 함께 쓰길 권장하는 로더로,CSS
를DOM
에 주입하는 로더 (정확히는, CSS를 DOM에 주입하는Javascript 코드로 변환
하는 역할)- 옵션의
injectType
을 통해 어떻게style
이DOM
에 주입될 지 지정할 수 있음- 하나 또는 여러 개의
<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