번들러를 비교해서 판단하기 전에 CommonJs 와 Es Module의 차이점에 대해 알고 있어야 한다.
JS모듈을 내보낼 때 2가지 방식을 사용한다. 그 내용은 아래와 같다.
CommonJS(CJS) 는 Node.js 에서 가장 일반적으로 사용되는 모듈 시스템이다.
(1) CJS의 특징
require()
로 접근하고 module.export
로 내보낸다.Tree-shaking
이 어려움(번들 크기에 영향)캐싱
(같은 모듈이 여러번 로드되어도 한번만 실행 -> 성능향상)동기적 로드
: 모듈이 필요한 시점에 즉시 로드되고 해당 모듈의 코드가 실행 될 때까지 다음 진행이 차단된다.(2) 예시코드
const {a, b} = require('index.js');
const c = 30;
const d = 40;
module.exports = {
c, d
}
ES Module(ESM)은 최신 Javascript 버젼에서 지원되는 모듈시스템으로
(1) ESM의 특징
import
로 접근하고 export
로 내보낸다.Tree-shaking
을 쉽게할 수 있다.정적 분석
: 빌드 타임에 모듈 의존성을 파악할 수 있어 필요한 모듈만 불러와 최적화 할 수 있다.import a, b from './index.js';
const c = 30;
const d = 40;
export {a, b};
Webpack의 핵심 가치는 번들링(통합)
이라는 목적에 있다. 개발자들이 '모든 자원을 하나로 통합하여 관리할 수는 없을까?'라는 고민으로부터 webpack이 뜨게 되었다.
webpack은 설정이 간편하고 직관적이다. 하나의 설정 파일에서 원하는 번들을 생성할 수 있도록 컨트롤할 수 있다. entry
와 output
을 명시하고, 어떤 plugin
과 loader
를 사용하여 파일들을 처리할지 명시함으로써 개발자는 번들링 프로세스를 유연하게 제어할 수 있다.
(1) Webpack 의 특징
많은 plugins과 loaders
쉽게 plugin과 loader를 부착할 수 있다. loader를 통해서 파일들을 변환, 번들링, 빌드를 진행하고 plugin을 통해서 output 파일을 튜닝해 준다.
Code splitting
번들 로드 최적화가 잘 되어 있다. 초기에 구동될 필요가 없는 코드를 분리하여 lazy loading을 통해 페이지 초기 로딩속도를 개선할 수 있다.
(2) 예시 코드
//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()],
};
(1) Rollup 특징
webpack 은 내부적으로 CommonJs를 사용하고, rollup은 ES Module
방식을 사용한다.
rollup은 빠르다. webpack은 모듈들을 함수로 감싸서 번들링하는 방식을 사용하지만 rollup은 모듈들을 호이스팅하여 한번에 번들링
한다.
rollup은 가벼운 번들을 생성한다.(라이브러리나 패키지 활용에 유리)
tree shaking
은 ESM에서 제대로 동작한다. rollup은 공식플러그인을 통해 ESM 형식으로 빌드 결과물을 출력할 수 있다.
진입점을 다르게 설정하여 번들링
가능하다. 진입점이 달라 중복해서 번들링 될 수 있는 부분을 알아내고, 독립된 모듈로 분리가 가능하다.(code splitting 측면에서 다른 번들러와 비교해 강점을 가짐)
(2) 예시코드
//rollup.config.js
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import { terser } from 'rollup-plugin-terser';
export default {
input: 'src/main.js',
output: {
file: 'public/bundle.js',
format: 'iife',
sourcemap: true,
},
plugins: [
resolve(),
commonjs(),
production && terser(),
],
};
위 번들러들은 내부적으로 javascript 를 기반으로 하여 번들링 한다. JS 언어의 성능상 한계 때문에 새로 등장한 번들러가 ESBuild이다.
esbuild는 자바스크립트를 위한 번들러이다. 타입스크립트의 타입 체킹이나 프론트엔드 언어 (Vue, Angular) 지원, 핫 모듈 리로딩을 포함한 개발 서버 오픈 등 번들링과 관계 없는 기능들은 일체 없다.
(1) ESBuild의 특징
Go
언어로 작성 됨(JS 기반 번들러보다 10~100 배 빠른 퍼포먼스)(2) ESBuild가 사용되지 못한 이유
vite는 빠르고 간결한 개발 경험에 초점을 맞춘 번들러이다. ESbuild 번들링을 통해 개발환경에서 높은 속도를 낸다.
(1) 기존 번들러의 문제점
브라우저에서 Native ESM
방식을 지원하기 전까지는 Javascript 모듈화를 브라우저 레벨에서 진행할 수 없었다. 그래서 해당 소스 모듈을 브라우저에서 지원하기 위해 require / IIFE / import, export를 통한 모듈화 문법을 이용하여서 여러개의 파일로 합쳐주거나 의미 있는 단위로 묶어주는 번들링 과정이 필요하다. 현재는 Webpack, Rollup 그리고 Parcel 도구들이 지원하고 있다.
애플리케이션이 점점 더 발전함에 따라서 JS 도구들의 성능 병목 현상이 발생, HMR 사용하더라도 변경된 파일 적용 시 수초가 걸려서 개발자 생산성을 떨어뜨는 문제가 발생하고 있다.
ESM(ECMAScript Modules)
ESM은 모듈화 문법인 import, export를 별도의 도구 없이 브라우저 자체에서 소화해 낼 수 있는 모듈 방식을 의미한다.
(2) Vite의 특징
import
가능0
에 가까움HMR 성능 개선 기존 번들러 기반에서는 소스 코드 변경 시 다시 번들링 과정을 다시 수행하다보니 성능이 느려지는 이슈를 Native ESM 방식을 사용하여 변경된 모듈만 교체해서 성능 개선 HTTP 헤더를 활용하여 페이지 로드 속도 개선(요청 횟수 최소화)
304 Not Modified: 클라이언트 캐시 사용
Cache-Control: max-age=31536000, immutalble 이용한 캐시 사용
(3) Vite의 문제
개발환경에서 ESBuild를 사용
하지만 프로덕션 환경에선 Rollup
으로 번들링을 수행한다. Rollup 설정에는 CommonJS처리, Polyfill 처리가 되어 있지 않아 일일이 플러그인을 설치해줘야 함
(플러그인 설정을 하게되면 Webpack이랑 빌드시간에서 크게 차이가 나지 않음)
개발 환경과 프로덕션 환경의 설정이 다르기 때문에 빌드 안정성이 낮음
(그래서 개발환경에서만 사용함)
CJS와 ESM 동작의 차이
require 함수는 동기적으로 작동하며, 코드 실행 중에 해당 모듈이 필요할 때 즉시 가져온다. 반면 ESM(ES Modules)에서는 import 및 export 구문이 동기적으로 실행되지 않고, 모듈 로딩이 비동기적으로 처리됩니다. 이는 코드의 동작 순서를 정확하게 예측하기 어렵게
만들고, 문제가 발생한 원인을 찾기 어렵게
할 수 있다.
Node.js의 호환성
프론트엔드 개발에서 프레임워크나 라이브러리의 설정 파일은 주로 Node.js 환경에서 실행된다. Node.js는 보편적으로 CommonJS(CJS)를 모듈 시스템으로 사용하기 때문에, 설정 파일을 사용할 때 별도의 설정 없이도 간편하게 활용할 수 있다.
https://chanyeong.com/blog/post/54