[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
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
를 사용
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
플러그인을 사용
- CRA 프로젝트 설정에서는
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 및 에셋
들을 제대로 처리하지 못함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 유틸리티
가 적용됨인라인 스타일
과 비슷하지만, 미리정의된 디자인(predefined design)을 사용할 수 있고호버/포커스 등 상태
및미디어 쿼리
를 사용할 수 있음
postcss
:Javascript 플러그인
을 사용해CSS 린팅
,변수
및믹스인
지원,최신 CSS 문법의 트랜스파일
등의 기능을 제공하는 변환기- 내부적으로
CSS
->Parser
->[Plugin 1, Plugin 2, ... ]
->Stringifier
->New CSS
로 이어지는 구조를 갖고 있음- CSS를
Tokenizer
와Parser
를 통해 분해 및 AST화 한 뒤, Plugin 들을 적용시키고 나온AST
를순수 CSS 문자열
로 변환함
- 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) 하는 등의 내용을 포함함
- nomarlize.css의 내용을 살펴보면,
sanitize.css
:normalize.css
의 모든 요소들이 포함되어 있으며, 나아가유용한 기본 설정(useful defaults)
을 포함하는 CSS 라이브러리- sanitize.css의 내용을 살펴보면,
커서
는 불명확한 인터페이스에 힌트를 주기 위해서만 변하도록 하거나(line 37),background-repeat
이 기본적으로 반복되지 않도록 하는(line 13) 등의 내용을 포함함
- sanitize.css의 내용을 살펴보면,
postcss-preset-env
:최신 CSS 문법
을 브라우저가 이해할 수 있도록변환
하고,타겟 브라우저 및 런타임
에 맞춰필요한 폴리필
을결정
하는PostCSS 플러그인
- MDN과 Can I Use로부터 버전 별 지원 기능 정보들을 가져와
browserlist
를 기반으로 기능의 변환이 필요한지 판단하며, 내부적으로autoprefixer
를 포함해 브라우저 지원 목록에 따라 필요할때만 접두사를 적용하도록 함 - CRA 프로젝트 설정에서는
autoprefixer: { flexbox: 'no-2009' }, stage: 3
라고 옵션을 지정하여최신
및IE 버전
의 flexbox 문법에만접두사를 추가
하도록 하며, 또한stage 3
에 해당하는 CSS 기능들을 변환하도록 설정- 참고1) Interactive Playground - PostCSS Preset Env - CSS Tools를 통해
browserlist
옵션 별 변환된 CSS를 확인할 수 있음 - 참고2)
flexbox
의 경우, 과거display:box
->display: flexbox
->display: flex
와 같은 변경을 거친 옵션임 “Old” Flexbox and “New” Flexbox | CSS-Tricks - 참고3) CSS 기능의 stage 수준을 확인하고 싶다면 CSS Database - CSS Tools을 확인할 것
- 참고1) Interactive Playground - PostCSS Preset Env - CSS Tools를 통해
- MDN과 Can I Use로부터 버전 별 지원 기능 정보들을 가져와
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.{확장자}
파일 에 대해 테스트를 수행하도록 설정되어 있음
- CRA 프로젝트 설정에서는
babel-jest
:코드 전처리(transform)
를 위해Babel
을 사용하도록 하는 Jest의 플러그인Javascript
/JSX
파일 테스트 전, 전처리 수행- CRA 프로젝트 설정에서는
babel-preset-react-app
프리셋을 사용하고, 해당 프리셋에서runtime
을classic
(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