지난번 웹사이트를 만들면서 CRA 대신 webpack을 직접 설정하여 구축하였다.
그렇게 했던 이유는
1. CRA는 다음과 같은 단점이 있다는 것을 익히 들었기 때문이다.
- 설정이 추상화되어 있어 세밀한 조정이 어려움.
- 실제로 사용하지 않는 설정이나 라이브러리 때문에 번들이 무거움
- 추가적인 커스터마이징을 하려면 eject 명령어로 CRA 설정을 해제해야 하며, 이는 복잡하고 관리가 어려워질 수 있음
2. webpack 직접 설정시 필요한 플로그인만 설정이 가능하여 프로젝트에 맞춘 번들 최적화가 가능하다고 해서,
3. 직접 해보며 공부해보고 싶어서.
그런데 프로젝트를 거의 완성을 앞두고나니 궁금해졌다.
정말로 직접 webpack 설정한 프로젝트가 CRA로 만든 프로젝트보다 번들사이즈가 작을까?
그래서 확인해봤다.
1. 'webpack 직접 설정 vs CRA' 번들 크기 비교
- CRA로 만든 프로젝트의 번들 크기
139.4KB
- webpack을 직접 설정한 프로젝트의 번들 크기
558.28KB
(같은 코드의 파일로 비교하였다)
결과는
CRA => 139.4KB
webpack 직접설정 => 558.28KB
CRA로 만든 프로젝트의 번들크기가 더 작았다.
당황스러웠다. 내가 이럴려고 고생해서 webpack을 직접 설정한게 아닌데말이다
추측을 해봤다
1. 측정을 잘못했나?
=> CRA, Webpack Docs 에 나와있는 방법으로 다시 했으나 똑같은 결과가 나왔다.
2. 필요하지 않은 webpack 플러그인이 과도하게 설정되어 있는가?
=> 필요한 것만 최소한으로 설정하였다.
2. 그럼 CRA로도 충분한데 왜 Webpack 설정을 직접 하는걸까?
위에서 말했듯,
webpack을 직접 설정하게 되면
필요한 플로그인만 설정이 가능하여 프로젝트에 맞춘 번들 최적화가 가능하다고 하였다.
여기서 내가 안일하게 생각하고, 너무 쉽게 간과했던 점이 있다.
바로 최적화..!
필요한 플러그인은 모두 설정했다고 생각했지만 사실은 아주아주 최소한의 설정이었다.
정작 빌드 속도를 높이고 번들사이즈를 줄이기 위한 과정이자 Webpack을 따로 설정하는 이유인 성능최적화를 생각하지 않았다.
(당연하게도 CRA를 만든 척척박사 개발자들이 번들 최적화와 자산처리 등을 기본탑재하지 않았을리가 없다)
3. 'Webpack 설정을 통해 직접 개발환경을 구성하려는 이유' 를 다시 정의했다.
필요한 설정들을 커스터마이징하여 프로젝트에 맞춘 최적의 성능, 최적화된 빌드를 구성하기 위해서
라고 할 수 있겠다.
webpack 설정을 통해 직접 구축하면 마냥 좋겠지 라고 했던 생각을 다시 고쳐잡고,
그럼 이제 리팩토링 해보려고 한다.
4. CRA의 webpack 설정을 까봤다.
CRA는 어떻게 webpack 설정이 되어있나 열어봤다.
방법은 CRA를 eject 하면 된다.
아래의 명령어를 터미널에 입력한다.
npm run eject
그러면 config 폴더에 webpack.config.js 파일을 통해 확인할 수 있다.
이외에도 Webpack, Babel, ESLint, etc. 앱을 build하기 위한 모든 구성요소들을 한번에 볼 수 있다.
주석까지 합치면 700줄 가량의 코드를 볼 수 있다.
그 중 최적화를 위한 설정을 일부 살펴보겠다.
5. webpack 설정을 다시 했다.
기존 코드
const path = require('path');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const webpack = require('webpack');
const dotenv = require('dotenv');
dotenv.config();
module.exports = {
devServer: {
historyApiFallback: true,
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,
host: 'localhost',
port: 3000,
hot: true,
},
entry: './src/index.jsx',
mode: 'development',
resolve: {
extensions: ['.js', '.jsx'],
},
module: {
rules: [
{
test: /.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
{
test: /\.(jpg|jpeg|gif|png|svg|eot|woff|ttf)$/i,
type: 'asset/resource',
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '',
},
},
'css-loader',
],
},
],
},
output: {
publicPath: '/',
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
clean: true,
assetModuleFilename: 'asset/[hash][ext][query]',
},
plugins: [
new HtmlWebPackPlugin({
template: './public/index.html',
filename: './index.html',
env: process.env,
}),
new CopyPlugin({
patterns: [{ from: 'src/assets', to: 'assets' }],
}),
new MiniCssExtractPlugin(),
new webpack.DefinePlugin({
'process.env': JSON.stringify(process.env),
}),
new BundleAnalyzerPlugin({
analyzerMode: 'static',
generateStatsFile: true,
}),
],
};
부끄럽지만 구글링을 통해 나름 설정해봤었다.
이제 여기에 몇가지 설정을 추가, 수정해보겠다.
- 추가된 설정
- 1. CssMinimizerPlugin 는 그대로 유지css 파일이 생성되지 않는다.
- 하지만 해당 프로젝트는 bootstrap이라는 외부 css 라이브러리를 사용한다. bootstrap은 css 파일이 생성되므로 CssMinimizerPlugin 이 그대로 사용되었다.
- Styled-component 의 경우 JS 코드로 인라인된 스타일을 <style> 태그에 삽입하긴 하기때문
- 삭제된 설정
수정된 코드
const path = require('path');
const webpack = require('webpack');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true
},
},
}),
],
},
devServer: {
historyApiFallback: true,
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,
hot: true,
host: 'localhost',
port: 3000,
},
entry: './src/index.jsx',
mode: 'production',
resolve: {
extensions: ['.js', '.jsx'],
},
module: {
rules: [
{
test: /.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
{
test: /\.(jpg|jpeg|gif|png|svg|eot|woff|ttf)$/i,
type: 'asset/resource',
},
],
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js',
clean: true,
assetModuleFilename: 'asset/[hash][ext][query]',
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
new HtmlWebPackPlugin({
template: './public/index.html',
filename: './index.html',
templateParameters: {
env: process.env,
},
}),
new CopyPlugin({
patterns: [{ from: 'src/assets', to: 'assets' }],
}),
new webpack.DefinePlugin({
'process.env': JSON.stringify(process.env),
}),
new BundleAnalyzerPlugin({
analyzerMode: 'static',
generateStatsFile: true,
}),
],
};
6. 번들 사이즈를 다시 비교해봤다.
- webpack 설정을 수정 한 뒤 번들 사이즈
141.31KB
결과는
558.28KB => 141.31KB
번들 사이즈가 74.7% 가량 줄어들었다.
7. 배운 점