🚀   새로운 블로그로 이전했습니다.

차세대 번들러 비교 및 분석 (feat. webpack, rollup, esbuild, vite)

2023.02.21
16분

번들러 란?

간단하게 설명하자면 여러개의 파일들을 하나의 파일로 묶어주는 도구입니다.

프론트엔드에서 번들러의 역사가 길다면 길고 짧다고 하면 짧다고할 수 있을 것 같습니다.
지금은 너무나도 당연하게 webpack vite 같은 번들러를 사용하여 프론트엔드 개발환경을 구성하고 있는데요.
왜 그런 것일까요? 브라우저의 역사를 조금 알면 이해할 수 있을 겁니다.

번들러가 필요해진 이유

태초에, 브라우저는 ES Modules지원하지 않았습니다.
다른 말로는 ES6 import 문법을 사용할 수 없고, <script> 간 모듈을 내보낼 수 없다는 것입니다.

초기에는 몇몇 스크립트 파일만 있으면 서비스할 수 있었지만,
웹서비스가 기하급수적으로 복잡해지면서 방대해진 코드들을 관리하기 어려워졌습니다.
개발자들은 다양한 전략으로 코드를 여러 파일로 분리하였고, window jQuery와 같은 전역객체를 활용하여 통해 스크립트 파일 간 모듈을 공유했습니다.

사람들은 웹 브라우저에서 더 많은 활동을 하게 되었고 더 좋은 서비스 경험이 요구 되었습니다.
더 작은 용량의, 더 최적화된 리소스를 제공해주기,
흩어져 있는 여러 파일을 하나로 통합하고 문자를 압축시키기,
다양한 사용자 환경에서도 돌아가도록 코드를 변환하기,
안 쓰이는 소스코드 부분을 분석해서 제거하기,
...
웹사이트 전처리 요구사항이 점점 많아졌고 이를 자동화시켜줄 친구가 필요했습니다.
여기서 grunt glub 같은 도구가 하나 둘 등장하기 시작했고 마침내 webpack의 시대가 도래하게 되었습니다.

2022 stateofjs 자료를 참고해보면 오늘 이 순간에도 번들러의 생태계가 빠른 속도로 변화되고 있습니다.

230221-132853

이 포스트에선 그 중 많이 알려진 webpack rollup esbuild vite에 대해 등장 순서대로 다뤄 볼 예정입니다.


Webpack

230304-140544

2012년 Tobias Koppers에 의해 webpack v1.0이 효율적으로 js파일을 통합해주는 도구로 공개되었습니다.
2016년 webpack v2.0이 릴리즈되고 ES6를 지원하여 커뮤니티의 큰 관심을 얻게 되었습니다.
2017년 webpack v3.0이 릴리즈되고 성능 개선, 기능 추가 되면서 사람들이 열광하기 시작했습니다.
2018년 webpack v4.0이 릴리즈되었는데 동시에 React, Anglular의 등장으로 SPA가 급부상하게 되면서 번들러의 역할 더욱 막중해지자 이른바 "대웹팩시대"가 도래하게 되었습니다.
현재는 2020-10-10에 릴리즈된 v5에서 지속적으로 업데이트되고 있습니다.

webpack의 정체성은 번들러의 목적인 "통합"에 있습니다.
'번들을 통합해서 관리할 순 없을까?'에 대한 고민이 webpack이 부상할 수밖에 없게 만들어 주었죠.

설정이 간편하고 직관적.
하나의 설정 파일에서 원하는 번들이 생성 될 수 있도록 컨트롤할 수 있습니다.
entryoutput을 명시하고 어떤 pluginloader로 파일들을 다룰 건지 명시하면 됩니다.

webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: {
    home: './pages/home.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: ["babel-loader"],
      },
    ],
  },
  plugins: [new HtmlWebpackPlugin()],
};

풍부한 plugins과 loaders.
webpack은 굉장히 강력한 개발 커뮤니티가 뒷바침해주고 있습니다.
쉽게 pluginloader를 부착할 수 있기 때문인데요. loader를 통해서 파일들을 변환, 번들링, 빌드를 진행하고 plugin을 통해서 output 파일을 튜닝해줍니다.
필요에 따라 다양한 plugin, loader가 오픈소스로 개발되고 있어

강력한 개발서버.
webpack은 Hot Module Replacement(HMR)를 처음으로 제안했고 2012년부터 사용되어왔습니다.
HMR는 소스코드의 변화를 감지하여 브라우저를 직접 새로고침할 필요가 없이 변화를 바로 반영해줍니다. 개발자는 덕분에 더욱 신속하게 개발할 수가 있게 되었죠.
webpack v2, v3, v4을 거쳐서 다양한 파일(css)의 변화도 감지할 수 있게 되었고 안정화되었습니다.

Code Splitting.

webpack의 꽃이라고도 할 수 있는 Code Splitting의 얘기도 빠질 수가 없습니다.

"Code splitting is one of the most compelling features of webpack."

한마디로 설명하자면 번들 로드 최적화하는 작업입니다.
파일들을 여러 번들 파일으로 분리하여 병렬로 스크립트를 로드하여 페이지 로딩속도를 개선할 수 있습니다.
추가로 초기에 구동될 필요가 없는 코드를 분리하여 lazy loading을 통해 페이지 초기 로딩속도를 개선할 수 있습니다.

끝으로 당시 같이 거론되었던 번들링 라이브러리에 대해서 비교하면서 webpack 설명을 마치겠습니다.

vs grunt

grunttask runner입니다.
minifying, 파일 통합, lint 적용 등 등록된 task를 순서대로 실행시켜 줍니다.
설정이 파편화되고 개발자가 직접 컨트롤해야하는 영역이 많습니다.
반면 webpackmodule bundler로 모듈을 통합하는 것에 초첨이 맞춰져 있습니다.

vs glup

glupgrunt와 흡사한 task runner입니다.
다만 stream-based로 파일을 접근하기에 grunt에 비해 성능이 더 우수하다고 평가를 받고 있습니다.


Rollup

webpack 시대 개막과 함께 2017년부터 개발이 시작된 module bundler입니다.
대세적으론 webpack의 열풍에 가려져 있었지만 점차 그의 매력이 인정을 받아 많은 차세대 번들러가 rollup을 벤치마킹하게 되었습니다.

v1.0 2018-12-28
v2.0 2020-03-06
v3.0 2022-10-11

Rollup의 정체성은 "확장"에 있습니다.

"compiles small pieces of code into something larger and more complex, such as a library or application"

작은 코드조각들을 거대하고 복잡한 어플리케이션 혹은 라이브러리로 만들어 준다고 스스로 소개합니다.
같은 소스코드를 다양한 환경에 맞춰 다르게 발드하는 것을 생각하면 될 것 같습니다.
그러면서 자연스럽게 아래 여론이 형성되었습니다.

"어플리케이션 만들 땐 webpack으로, 라이브러리 만들 땐 rollup으로!"

rollup의 사용방식과 구성방식은 webpack과 거의 흡사합니다.
inputentry를 설정하고 번들링에 적용할 기능을 plugin 형태로 끼워 넣으면 됩니다.

rollup.config.js
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import { terser } from 'rollup-plugin-terser';

const production = !process.env.ROLLUP_WATCH;

export default {
  input: 'src/main.js',
  output: {
    file: 'public/bundle.js',
    format: 'iife',
    sourcemap: true,
  },
  plugins: [
    resolve(),
    commonjs(),
    production && terser(),
  ],
};

webpack과의 차이를 비교해보면 더 정확하게 rollup의 특성을 이해할 수 있습니다.

vs webpack

한마디로 정리하면,
webpack은 내부적으로 Commonjs를 사용하고 rollup은 typescript(ES6)를 사용합니다.
이로 인해서 아래 특성들이 있다고 볼 수 있습니다.

rollup은 ES6 모듈 형태로 빌드할 수 있습니다.
webpack은 CommonJS 형태로만 번들링이 가능했습니다. 물론 webpack v5부턴 ES6로 번들할 수 있습니다.
라이브러리는 ES6 번들에 생성에 대한 수요가 강합니다.
ES6 환경에서는 ES6 번들이 사용되고 CommonJS 환경에서는 CommonJS 번들이 사용되도록 해줘야 라이브러리 사용자는 더욱 최적화된 애플리케이션을 빌드해줄 수 있습니다.

rollup은 좀 더 빠릅니다.
webpack은 모듈들을 함수로 감싸서 평가하는 방식을 사용하지만 rollup은 모듈들을 호이스팅하여 한번에 평가하기에 성능상 이점이 있습니다.

rollup은 더 가벼운 번들을 생성합니다
tree shaking은 기본적으로 ES6 코드에서 제대로 동작합니다.
단순히 레퍼런스되지 않는 코드를 제거하는 것이 아닌 사용되는 모듈만 AST 트리에 포함시키는 방식으로 불필요한 코드를 제거하기 때문입니다.
rollup은 공식 플로그인을 통해서 CommonJS 코드를 ES6 코드로 변환할 수도 있습니다.

rollup은 CommonJS 코드를 ES6코드에서 사용할 수 있습니다.
기본적으로 ES6에서는 CommonJS식의 require를 지원하지 않습니다.
webpack에서도 공식적으론 ES6 혹은 CommonJS 한 형태의 코드 베이스를 사용하기를 권장합니다.


ESBuild

앞서 봐왔던 번들러는 모두 내부적으로 JavaScript을 기반으로 번들링을 합니다.
따라서 JavaScript 언어가 가질 수 밖에 없는 성능상의 한계가 있습니다.
그 한계를 뿌수고자하는 움직임이 있었으니 그 라이브러리가 바로 ESBuild 입니다.

ESBuild는 내부적으로 Go로 작성되었고 JS 기반의 번들러보다 10배에서 100배까지 빠른 엄청난 퍼포먼스를 보여줬습니다. 빠른 이유에 대해서는 아래와 같이 소개하고 있습니다.

문제는 2020년도에 파격적으로 등장 했지만 아직까지도 메이저 버전이 릴리즈 되지 못했습니다. (현재 기준 v0.17.3)
사실 번들러로서 필수적인 기능들이 갖춰졌습니다.

  • JavaScript, CSS, TypeScript, and JSX built-in
  • Bundles ESM and CommonJS modules
  • Tree shaking, minification, and source maps
  • Local server, watch mode, and plugins

하지만 설정이 webpack, rollup처럼 유연하지 못하고 아직 안전성 관련 이슈가 있습니다.

  • code splitting 및 CSS와 관련된 처리가 아직 미비합니다.
  • esbuild는 es5 이하의 문법을 아직 100% 지원하지 않습니다.

Vite

ESBuild의 단점을 보완시킨 라이브러리 Vite(비트) 입니다.
Vue.js의 창시자인 Evan You가 만든 frontend build tool 입니다.

2021년에 등장했지만 벌써 v4가 나온 만큼 굉장히 활발하게 업데이트 되고 있는 라이브러리입니다.
v1 rc에서 바로 v2 alpha로 넘어간 것 같습니다.
v2.0.0 2021-02-16
v3.0.0 2022-07-13
v4.0.0 2022-12-09

vite를 2가지 키포인트로 설명할 수 있을 것 같습니다.

  • native ES modules 기반의 강력한 개발서버
  • esbuild로 파일들을 통합하고 rollup을 통해 번들링

전반적으로 esbuild로 성능을 극적을 끌어오고 rollup을 통해서 번들링의 유연성을 챙겼다고 볼 수 있습니다.
공식문서 (Vite를 사용해야 하는 이유)에서 자신에 대해 너무 잘 설명하고 있어 꼭 한번 정독해보시길 바랍니다!

특성을 아래와 같이 요약할 수 있을 것 같습니다.

  • 개발 서버 구동 시간이 거의 0에 가까움,
  • 모든 CommonJS 및 UMD 파일을 ESM으로 불러올 수 있도록 변환함
  • 별도의 설정이 없이 다양한 리소스 import 가능
  • CSS 빌드 최적화
    • Direct Import 구문에 대해 Preload 하도록 함으로써, 네트워크 비용 줄임

주의 해야할 점

vites는 기본적으로 ES6을 타겟으로 번들을 생성합니다.
따라서 ES5이하로 타겟을 잡으려면 별도로 polyfill를 다뤄야 합니다.
그래도 공식적으로 @vitejs/plugin-legacy 플러그인을 제공 해줍니다.

vite는 기본적으로 <root>/index.html 파일이 빌드를 위한 진입점입니다.
순수한 JS 번들을 생성하기 위해서는 라이브러리 모드를 설정해야 합니다.
( 3.2.0 에서야 멀티 entry를 지원하게 되어 고생한 기억이 있습니다... )


참고한 문헌

http://2016.stateofjs.com/2016/buildtools/
https://2017.stateofjs.com/2017/build-tools/results
https://2018.stateofjs.com/other-tools/
https://2019.stateofjs.com/other-tools/#build_tools
https://2020.stateofjs.com/en-US/technologies/build-tools/
https://2021.stateofjs.com/en-US/libraries/build-tools/
https://2022.stateofjs.com/en-US/libraries/build-tools/

https://github.com/webpack/webpack/releases
https://github.com/rollup/rollup/blob/master/CHANGELOG.md
https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md
https://yoon-dumbo.tistory.com/entry/롤업과-웹팩의-차이점-rollup-vs-webpack
https://so-so.dev/web/tree-shaking-module-system/