[TIL] 번들러의 개념과 차세대 번들러 비교 및 분석

HyeLin·2023년 9월 11일
0
post-thumbnail

🧳 Bundler의 탄생

이전의 웹사이트

초창기 웹은 웹 사이트의 규모가 크지 않았다. 따라서 웹을 구성하는 HTML, JavaScript 파일의 규모가 지금에 비해 훨씬 작았고 서비스의 유지보수도 어렵지 않았다.
But, 점점 기술이 발전하면서 파일 하나 당 코드의 양과 웹을 구성하는 파일의 수가 커지게 되었고, 하나의 서비스에 어마어마한 양의 JavaScript 파일을 다루게 되었다. 이로 인해, 문제점이 생기기 시작했다.

✔️ 중복된 이름으로 인한 충돌

// app.js
var num = 10;
function getNum() {
  console.log(num);
}
// main.js
var num = 20;
function getNum() {
  console.log(num);
}
<!-- index.html -->
<html>
  <head>
    <!-- ... -->
  </head>
  <body>
    <!-- ... -->
    <script src="./app.js"></script>
    <script src="./main.js"></script>
    <script>
      getNum(); // 20
    </script>
  </body>
</html>

규모가 큰 웹페이지의 경우, 많은 개수의 JS 파일로 구성되고 여러 사람이 웹 서비스 개발에 참여하다보니 서로 만든 함수명, 변수명이 겹치는 일이 빈번하게 됩니다. 이런 경우, 언제 어디서 충돌하게 될지 모르는 위험이 있다.

✔️ 파일 전송 속도 문제
사용자가 브라우저에 URI를 입력하면 서버는 그에 해당하는 파일을 사용자에게 제공한다. 웹 애플리케이션을 구성하는 파일의 양이 많을 수록, 사용자에게 해당 파일을 제공하는 데에 시간이 오래 걸린다.

사용자들은 더 좋은 서비스 경험을 요구하게 되었다.
〰️ 더 작은 용량의, 더 최적화된 리소스를 제공해주기
〰️ 흩어져 있는 여러 파일을 하나로 통합하고 문자를 압축시키기
〰️ 다양한 사용자 환경에서도 돌아가도록 코드를 변환하기
〰️ 안 쓰이는 소스코드 부분을 분석해서 제거하기

→ 이를 자동화 시켜줄 무언가가 필요해!

📂 번들러의 등장

위의 문제들을 해결하기 위해, 여러개의 파일을 하나로 묶어주는 '번들러'가 등장한다. 대표적으로 webpack 이 주로 사용된다.

1. Webpack

  • 웹팩의 목적은 번들러의 목적인 '통합'에 있다.
  • 서로 연관 관계가 있는 웹 자원들을 js, css, img와 같은 static한 자원으로 변환해준다.

특징

1. 설정이 간편하고 직관적
하나의 설정 파일에서 원하는 번들이 생성될 수 있도록 컨트롤할 수 있다.

// webpack.config.js
const path = require('path'); // 모듈을 불러온다
const HtmlWebpackPlugin = require("html-webpack-plugin"); // HTML 파일을 자동 생성하게 한다.

module.exports = { // 이 객체를 내보내서 Webpack이 설정을 읽을 수 있게 한다
  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"], // .js파일에 대해 'babel-loader'를 사용하여 ES6+ 코드를 일반 JS 코드로 변환. node_modules는 제외
      },
    ],
  },
  plugins: [new HtmlWebpackPlugin()], // 이 플러그인은 dist 폴더에 HTML 파일을 만들고, 그 안에 빌드된 JS 파일을 자동으로 연결
};

2. 풍부한 plugins과 loaders
웹팩은 plugin와 loader를 쉽게 부착할 수 있다.
loader를 통해서 파일들을 변환, 번들링, 빌드를 진행하고. plugin을 통해서 output 파일을 튜닝해준다.
필요에 따라 다양한 plugin과 loader가 오픈소스로 개발되고 있다.

3. 강력한 개발 서버
웹팩은 Hot Module Replacement(HMR)를 2012년부터 사용했다. HMR은 일부 모듈의 변경만 감지하여 페이지 갱신 없이 변경사항을 브라우져에 렌더링한다. 이로인해, 개발자는 신속하게 개발이 가능하다.

4. Code Splitting

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

웹팩의 꽃. 번들 로드 최적화하는 작업이다. 파일들을 여러 번들 파일로 분리해서, 스크립트를 병렬로 로드하여 페이지의 로딩 속도를 개선할 수 있다. 추가로 초기에 구동될 필요가 없는 코드를 분리하여 lazy loading을 통해 페이지 초기 로딩속도를 개선할 수 있다.

2. rollup.js

특징

1. 코드를 동일한 수준으로 올리고(Scope Hoisting) 한 번에 번들링
한 번에 하기때문에 속도는 웹팩보다 빠르고 번들링 결과물도 가볍다. 그러나 변수 충돌에 있어서는 웹팩보다 덜 안정적.

2. es 모듈 형태로 빌드 가능 -> 라이브러리, 패키지 작업에 활용
웹팩은 es 모듈로 번들 불가능. 오직 commonJS 형태로 번들링 진행 가능하다. 트리쉐이킹이 가능하다는 것이 라이브러리에 가장 적합한 번들러이다. 사용자가 라이브러리 코드 일부만 사용했는데, 전체 결과가 번들 결과물에 포함된다면 불필요하게 용량을 증가시키기 때문이다.

→ 라이브러리 제작 시 〰️ rollup
→ 웹 어플리케이션 제작 시 〰️ webpack

ES Modules

import moduleName from 'module'; // import 명령어를 통해 가져온 변수는 읽기 전용
export default moduleName;
  • 비동기와 정적 로딩을 지원. 따라서 트리 쉐이킹(Tree Shaking)이 가능하며, 빌드 시 불필요한 코드 제거 가능
  • 주로 브라우저 환경에서 사용. 최근 버전의 모던 브라우저는 대부분 ES Modules를 지원한다.
    Common JS
const moduleName = require('module'); // require를 통해 가져온 객체는 수정가능
module.exports = moduleName;
  • 동기와 동적 로딩을 지원. 이는 서버 측에서 빠른 모듈 로딩을 가능하게 하지만, 트리 쉐이킹이 어렵다.
  • Node.js 환경에서 널리 사용된다.

3. 진입점을 다르게 설정하여 번들링 가능
진입점이 다르기 때문에 중복되어 번들링될 수 있는 부분을 알아내고, 독립된 모듈로 분리가 가능하다. 따라서 code splitting에서 강점을 보인다.

3. ESBuild


앞서 봤던 번들러는 모두 내부적으로 JavaScript를 기반으로 번들링한다. 따라서 JS언어가 가지는 성능상의 한계가 있다. 그 한계를 뿌시고자 ESBuild가 등장했다.

특징

1. 웹팩보다 100배 빠른 속도
그 이유는 GO로 작성되었기 때문이다. JS는 인터프리터 언어이기 때문에 한줄한줄 기계어로 변환을 한다. 반면에 GO는 컴파일 단계에서 미리 소스코드를 전부 기계어로 변환해놓는다.
또한, JS는 싱글 스레드 기반이라 한 파일씩 순차적으로 처리, GO는 멀티 스레드 기반으로 동작할 수 있다. 즉, ESBuild는 코드 파싱, 출력, 소스맵 생성을 모두 병렬로 처리하여 빠른 속도 보장.

2. 개발자 편의 제공하지 않음
애초부터 자바스크립트를 위한 번들러이다. 그저 빌드 도구일 뿐, 타입 체킹이나 HMR 등 번들링과 상관없는 기능들은 전혀 없다.
→ d.ts 지원되지 않음(별도의 타입 체킹 x)
→ 코드 분할 및 CSS 관련 처리 미비
→ 보통 프레임워크를 기반으로 하는 웹 개발보다는 바닐라 스크립트 형태의 라이브러리에 적합

4. Vite


Vue의 창시자가 Vite를 제작했다. 처음에는 Vue를 위해 만들어졌으니, 지금은 React 등 다른 프레임워크와 라이브러리에서도 지원이 가능하다. 웹팩으로 빌드하는 CRA를 사용하다가 Vite를 사용하면 말로 표현할 수 없을 정도의 개발 경험을 누릴 수 있다.

특징

1. ES Modules 기반의 강력한 개발서버
번들링 속도가 매우 빠르다. 개발서버 구동시간이 거의 0에 가깝다. ESM을 이용해 소스코드를 제공하도록 하고 있다. 즉, 브라우저가 곧 번들러라는 뜻이다. vite는 그저 브라우저의 판단 아래 특정 모듈에 대한 소스코드를 요청하면 이를 전달할 뿐이다.

2. 기본 ES 모듈보다 매우 빠른 HMR(Hot Module Replacement)제공
번들러가 아닌 ESM을 이용해서 제공하기 때문에, 어떤 모듈이 수정되면 vite는 그저 수정된 모듈과 관련된 부분만을 교체할 뿐, 브라우저에서 해당 모듈을 요청하면 교체된 모듈을 전달할뿐. 이 과정에서 ESM을 이용하기 때문에 앱 사이즈가 커져도 HMR을 포함한 갱신 시간에는 영향을 끼치지 않는다.

3. Esbuild로 파일들을 통합하고 Rollup을 통해 번들링
esbuild로 성능을 극적으로 끌어오고 rollup을 통해서 번들링의 유연성을 챙겼다.

4. CSS 빌드 최적화
Direct Import 구문에 대해 Preload 하도록 함으로써, 네트워크 비용을 줄였다.

import { something } from 'some-module';

*Preload
Preload는 웹 브라우저가 페이지를 렌더링하기 전에 필요한 리소스를 미리 로딩하는 것. 이를 통해 사용자가 실제로 해당 리소스를 요청할 때 이미 미리 로딩되어 있어서 빠르게 제공할 수 있다.


But!
배포를 위해서는 기존 번들과정이 필요하다. 아직 ESM을 프로덕션에 배포하는 것은 비효율적. 또한, 트리쉐이킹이나 코드 스플리팅같은 최적화 기능 제공하지 않는다. 따라서 기존의 번들링 기법을
최대한 활용하고, 내부적으로는 rollup 사용.

🔥 결론

webpack
광범위한 개발 레퍼런스를 활용하고 많은 서드파티를 필요로 하는 복잡한 어플리케이션이라면.

rollup
ES6 모듈 형식으로 빌드 결과물을 출력하여 라이브러리나 패키지에 활용하고 싶다면. 트리 쉐이킹과 같이 효율성을 고려해야 하는 프로젝트라면.

vite
SPA를 생성하기 위한 Vue CLI/CRA를 대체할 거리를 찾고있다면. (특정 프레임워크에 종속되지 않고 다양한 프레임워크/라이브러리에 대한 템플릿 제공)

ES Build
기능보다는 속도가 중요한 작은 프로젝트. 고급 기능이나 플러그인 생태계, 복잡한 코드 분할 등을 필요로 하는 대규모 프로젝트에서는 안된다.

profile
개발자

0개의 댓글