이전에 작성한 1편에서 babel-loader를 적용했는데요. 좋은 기회가 생겨 면접을 봤는데 자연스럽게 지나갔던 부분들에 대해 답변하지 못하여 저 자신에게 많이 실망했습니다. 반성하며, Loader와 Plugin이 무엇인지, 무엇이 다른지부터 파헤쳐 볼게요.
Loader
웹팩은 각 파일을 확장자(css, image 등)가 아닌 모듈로써 바라봅니다. 위 예시로 보자면 import를 통해 모듈(css파일)을 불러옵니다. 어떻게 적용되는걸까요?
위 코드는 webpack config에서 설정할 내용입니다. 구문은 잠시 뒤에 설명하고 간단하게 보자면 .css 가 달린 파일(모듈)은 css-loader 에 정의된 함수를 실행하도록 되어있습니다. 함수는 css파일을 로드하여 자바스크립트 모듈로 변환되도록 되어있습니다. 이때 @import, url() 은 자바스크립트의 import나 require처럼 동작하도록 해석하고 처리합니다.
해당 css가 html에 style, 혹은 청크 단위의 css파일로 분리가 되어 link태그로 연결될 수도 있는데 이건 또 다른 (style-loader, MiniCssExtractPlugin) 로더를 무엇을 설정하느냐에 따라 달라집니다.
즉 로더는 함수를 내보내는 노드 모듈 로서 모듈이 번들링 되는 과정에서 해당하는 모듈이 처리될 때 실행되는 함수라고 정의할 수 있습니다.
Plugin
loader는 특정 모듈을 변환하는데 사용되지만 plugin은 좀 더 넓은 범위로써 번들을 최적화하거나, 에셋관리, 환경변수 주입같은 광범위한 작업에 사용됩니다. 쉽게 말해 추가적인 기능을 제공하는거죠.
여기서 꼭 알아야 할 부분은 Loader는 번들링 중에 모듈을 확인할 때 실행되지만 Plugin은 webpack lifecycle hooks 단계별로 원하는 타이밍에 코드를 실행시킬 수 있습니다.Compiler Hooks
Config 구성
저는 webpack 설정을 dev 환경과 production 환경으로 분리할 예정인데요. 이전에 만든 webpack.config.ts 파일을 공통적인 설정을 모아놓은 config로 사용하고 환경별 설정 파일을 분리해서 만들겠습니다.
TsConfig
tsconfig에 적용된 속성들은 공식문서에 모두 나와있기에 따로 설명하지는 않겠습니다! tsconfig공식문서
webpack.config.ts
plugins
TsconfigPathsPlugin : tsconfig.json에 정의된 경로를 사용하여 모듈을 해석하는 플러그인
ProvidePlugin : 모듈을 자동으로 가져오는 플러그인 (react를 넣음으로써 import 구문없이 모듈을 자동으로 가져오게 함)
CleanWebpackPlugin : 번들링 전에 기존 번들 파일을 정리해주는 플러그인 (dist 폴더 정리)
HtmlWebpackPlugin : 번들링된 결과물을 html에 자동으로 주입하는 플러그인 옵션 참고 내용
CopyWebpackPlugin : 파일을 복사하는 플러그인 (public 폴더 복사)
ForkTsCheckerWebpackPlugin : 타입 체크를 별도의 프로세스로 실행하여 빌드 속도를 향상시키는 플러그인
ESLintPlugin : 빌드 단계 & 런타임 단계에서 자바스크립트 코드 검사를 위한 플러그인
loader
babel-loader : 자바스크립트 파일을 번들링하기 전에 babel을 사용하여 트랜스파일링하는 로더
ts-loader : 타입스크립트 파일을 번들링하기 전에 타입스크립트 컴파일러를 사용하여 트랜스파일링하는 로더
@svgr/webpack : svg를 React 컴포넌트로 가져올 수 있게 해주는 로더
webpack.dev.ts
개발환경 webpack 설정입니다. webpakc-merge를 사용하여 공통 설정을 먼저 불러온 후 개발환경 설정을 추가합니다.
configDotenv({ path: "./.env.development" }) 개발환경에서 사용할 환경변수를 불러옵니다.
개발환경에서 빠른 디버깅을 위해 devtool을 inline-source-map으로 설정했습니다. (앱이 커질 경우 inline-source-map 사용 시 속도가 느려질 수 있습니다.)
CSS는 css-loader와 style-loader를 사용하여 html에 inline 스타일로 주입되도록 설정했습니다. (style-loader 사용 시 리빌드 속도가 빠르다.)
webpack.prod.ts
프로덕션 환경에서는 코드를 최적화하고 콘솔로그를 제거하는 작업을 합니다. 하나씩 살펴볼까요?
minimize : minimizer에 정의된 플러그인을 사용하여 코드를 최적화합니다.
CssMinimizerPlugin : CSS를 최적화하는 플러그인
TerserPlugin : 자바스크립트 코드를 최적화하는 플러그인
splitChunks : 공통 모듈을 나누어 번들링하는 플러그인
html-loader : html 파일을 컴파일 단계에서 최소화 하는 로더
MiniCssExtractPlugin : CSS를 추출하여 별도의 파일로 생성하는 플러그인
Chunk ? Vendor ?
Chunk는 하나의 덩어리입니다. SPA의 문제점인 초기 로딩 속도를 개선하기 위해 Chunk 단위로 나누어 번들링하는 겁니다!
Vendor는 청크간에 겹치는 패키지들을 모아 별도의 파일로 추출해주는 코드입니다. A 청크에 (a, b) 패키지가 있고 B 청크에 (a, c) 패키지가 있으면 vendor에는 (a) 패키지가 있게 됩니다.
또 하나 outfut쪽 [name], [chunkhash], [contenthash] 등의 옵션을 사용하여 파일명을 지정할 수 있습니다. 각각의 특징이 있는데 간단하게 요점만 살펴보자면
[name]
[name] : entry에 정의된 이름을 사용합니다. (default : main)
[hash]
[hash] : 더 이상 지원되지 않습니다.
[chunkhash]
첫 빌드 시
CSS만 변경 후 빌드 시
CSS만 변경했음에도 js 파일이 변경되었습니다. chunkhash는 entry 기준으로 해시를 생성하기에 js 파일은 변경되지 않았음에도 불구하고 해시가 변경되었습니다.
[contenthash]
entry 기준 [name]에 따라 동일한 main이란 이름을 쓰고있는 css, js 파일은 각각 다른 해시를 가지고 있습니다. contenthash는 파일 변경 사항에 따라 해시가 생성되기에 변경 된 파일만 해시가 변경됩니다.
저는 contenthash가 가장 합당하다고 생각하기에 contenthash를 사용했습니다.
마무리
이번 2편에서는 웹팩 설정에 사용되는 개념들을 살펴보고 개발환경과 프로덕션 환경을 구성해보았습니다. 요즘 이직을 준비하며 면접도 보고있늗네 나름 공부를 열심히 했다고 생각했음에도 면접에서 답하지 못한 부분들이 많아 아쉬움이 많이 남습니다.. 뭐 부족한 부분도 깨달았고 더 성장할 수 있는 계기가 된거같아서 만족하려고 합니다! 잘못된 부분이나 더 좋은 방향이 있다면 언제든 댓글에 남겨주세요. 감사합니다 :)