Webpack 3 코드 스플리팅으로 번들 사이즈 40% 줄인 후기

문제 상황

운영 중인 관리자 페이지의 초기 로딩 시간이 5초를 넘어가기 시작했다. 번들 사이즈를 확인해보니 main.js가 2.3MB에 달했다. 모든 라우트의 컴포넌트와 라이브러리가 하나의 파일로 묶여있었다.

해결 과정

1. webpack-bundle-analyzer 도입

먼저 어떤 모듈이 용량을 차지하는지 파악했다.

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
};

moment.js의 로케일 파일들과 lodash 전체가 포함되어 있었다.

2. CommonsChunkPlugin 설정

vendor 번들을 분리했다.

module.exports = {
  entry: {
    app: './src/index.js',
    vendor: ['react', 'react-dom', 'react-router']
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: Infinity
    })
  ]
};

3. 라우트별 코드 스플리팅

React Router와 dynamic import를 조합했다.

import Loadable from 'react-loadable';

const Dashboard = Loadable({
  loader: () => import('./pages/Dashboard'),
  loading: () => <div>Loading...</div>
});

const UserManagement = Loadable({
  loader: () => import('./pages/UserManagement'),
  loading: () => <div>Loading...</div>
});

4. 불필요한 모듈 제거

moment.js는 IgnorePlugin으로 불필요한 로케일을 제외했다.

plugins: [
  new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
]

lodash는 필요한 함수만 개별 import로 변경했다.

// Before
import _ from 'lodash';

// After
import debounce from 'lodash/debounce';
import throttle from 'lodash/throttle';

결과

  • main.js: 2.3MB → 850KB
  • vendor.js: 550KB (새로 생성)
  • 초기 로딩: 5.2초 → 2.1초
  • 라우트별 chunk: 50~200KB

캐싱 효과까지 고려하면 재방문 시 로딩 속도는 더 개선되었다. 코드 스플리팅은 이제 프로젝트 초기부터 고려해야 할 필수 요소라는 것을 체감했다.