Bundle 사이즈를 줄이기 위해 webpack 공식 문서, github 유명 레포지토리 및 다른 사람들의 webpack 코드, 여러 블로그를 찾아보며 webpack 최적화를 적용해 보았다. 내가 적용한 것들을 기록해 본다. 적용한 대상은 Vanilla JavaScript로 구현한 SPA이다.
1. 불필요한 라이브러리, 사용이 적지만 큰 용량을 차지하는 라이브러리 제거
webpack-bundle-analyzer를 사용하면 페이지 하단 결과처럼 번들 파일을 분석해 준다. 어떤 요소가 얼마만큼의 크기를 차지하는지 알 수 있다. 이를 통해 불필요한 라이브러리나 사용자 적지만 큰 용량을 차지하는 라이브러리를 제거할 수 있다. 보통 CRA를 사용하면 lodash와 같이 기본으로 설치되는 라이브러리들이 꽤 많은 용량을 차지하고 있다고 한다. 사용하지 않으면 제거해야 한다.
나는 사용 중인 icon 라이브러리가 스타일에 따라 bold, regular 두 가지가 있었는데 각 라이브러리가 생각보다 큰 용량을 차지하고 bold의 경우 한 두 곳에서만 사용되고 있어 제거하기로 했다. 라이브러리를 정리하는 것만으로도 사이즈 변화가 꽤 크다.
2. CSS 파일 분리 및 압축
1) optimization
CssMinimizerPlugin 사용
2) css 분리
MiniCssExtractPlugin 사용
webpack.prod.js
const { merge } = require('webpack-merge');
...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const commonConfig = require('./webpack.common');
const prodConfig = {
mode: 'production',
optimization: {
minimizer: [new CssMinimizerPlugin(), '...'],
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
},
],
},
plugins: [
new MiniCssExtractPlugin({
linkType: false,
filename: '[name].[contenthash:6].css',
chunkFilename: '[name].[id].[contenthash:6].css',
}),
...
],
};
module.exports = merge(commonConfig, prodConfig);
3. Code Splitting
1) chunk
webpack.common.js
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].[contenthash:6].js',
chunkFilename: '[name].[id].[contenthash:6].js',
path: path.resolve(__dirname, 'dist'),
clean: true,
publicPath: '/',
},
...
}
기존에 bundle.js로 하나였던 output을 chunk로 분할했다.
2) dynamic import 적용
- 페이지 동적 import
import Home from '@pages/Home';
export const getView = (name) => import(`./${name}`).then((mod) => mod.default);
export const routes = [
{ path: /^\/$/, name: 'Home', view: Home },
{ path: /^\/post\/[\w]+$/, name: 'Post' },
{ path: /^\/list\/[\w]+$/, name: 'ListInfo' },
...
];
빠르게 로드되어야 하는 홈 페이지를 제외한 페이지를 동적으로 import 했다.
- 특정 컴포넌트에서만 사용되는 라이브러리 동적 import
@libs/cropper.js
const importImageCropper = async () => (await import('cropperjs')).default;
export default importImageCropper
동적 import한 cropper를 export 한다.
import Component from '@components';
import importCropper from '@libs/cropper';
import 'cropperjs/dist/cropper.min.css';
export default class ImageCropperModal extends Component {
...
init() {
const image = this.$target.querySelector('#image');
importCropper().then((Cropper) => {
this.$cropper = new Cropper(image, {
viewMode: 1,
aspectRatio: 1,
autoCropArea: 1,
});
})
}
...
}
export한 라이브러리를 가져와 사용한다. 함수를 호출하고 반환값(실제 라이브러리)을 구현에 사용한다.
최적화 결과
번들 사이즈를 약 42.35% 줄일 수 있었다.
webPageTest
전반적으로 성능이 개선되었다.
webpack에는 여러 플러그인이 존재하고 그 안에 또 많은 설정들이 존재한다. 그렇기에 webpack을 설정하는 방법도 너무나도 다양하다. 최적화를 위해 여러 코드를 보며 따라 해 보았는데 어떤 것은 적용한 것이 이전보다 못한 결과가 나오기도 하고.. 정말 많은 시행착오를 겪었다. 내가 발견하지 못한 또는 잘못 설정하여 놓친 최적화 방법들도 있을 텐데 더 많은 방법들을 찾아보고 시도해보고 싶다.
참고
https://webpack.kr/guides/code-splitting/
https://webpack.kr/plugins/mini-css-extract-plugin/#root
https://webpack.kr/plugins/css-minimizer-webpack-plugin#root
https://tech.kakao.com/2023/06/13/fe-performance-improvement-2/
'Develop' 카테고리의 다른 글
[Husky] Git Hooks 설정을 통한 커밋 전 코드 포맷팅 자동 적용하기 (Lint-Staged) (0) | 2024.01.18 |
---|