웹 애플리케이션을 구성하는 각각의 파일(모듈)들을 하나의 파일로 합치는 것을 번들링(bundling)이라고 합니다. 빌드(build)도 유사한 의미로 사용됩니다.
웹 애플리케이션의 크기가 커지면 유지 보수가 쉽도록 한 파일을 여러 개로 분리해야 합니다. 이때 분리된 파일 각각을 모듈(module)이라고 하고 파일을 모듈로 나누어 관리하는 것을 모듈 시스템이라고 합니다.
자바스크립트는 기본적으로 모듈 시스템을 지원하지 않습니다. 다시 말하면, 자바스크립트는 파일 스코프와 import, export를 지원하지 않습니다. 자바스크립트에서는 <script> 태그를 사용하여 외부의 자바스크립트 파일을 로드할 수 있지만 분리된 자바스크립트 파일들은 모두 하나의 자바스크립트 파일 내에 있는 것처럼 동작합니다. 따라서 분리된 자바스크립트 파일들의 전역 변수가 중복되는 문제가 발생할 수 있습니다. 이러한 한계를 극복하기 위해 webpack등의 모듈 번들러가 등장합니다.
<!-- index.html -->
<!DOCTYPE html>
<script src="foo.js"></script>
<script src="bar.js"></script>
// foo.js
const user = "foo";
console.log(user);
// bar.js
const user = "bar";
console.log(user); // ERROR
나눠놓은 파일을 다시 합쳐야 하는 이유는 웹 애플리케이션의 로딩 속도와 성능 문제 때문입니다. 웹 페이지가 복잡해질수록 html, css, js 파일 이외에 웹 폰트, favicon 이미지, JSON 데이터 등 많은 파일을 요청하고 응답 받습니다. 받아와야 하는 파일의 개수가 늘어나면 초기 웹 페이지 로딩 속도가 느려지고 사용자가 페이지를 떠날 확률이 높아집니다. 따라서 번들링을 통해 파일을 압축하고 병합하는 작업을 진행하여 파일 개수를 줄이는 것입니다.
모듈 번들러는 여러 자바스크립트 파일을 하나의 큰 파일로 결합하는 번들링을 합니다. 번들러는 모든 것을 결합하는 방법을 추적하는 종속성(dependency) 그래프를 만듭니다. 웹팩에 entry point를 명시하면 import과 dependency를 가지고 모든 것을 하나의 자바스크립트 파일로 결합합니다.
// Add dependencies(package.json)
npm init -y
// Install Lodash
npm install lodash
// Install webpack
npm install --save-dev webpack webpack-cli
// package.json
"scripts": {
"build": "webpack",
"dev": "webpack serve"
}
// Build and Compile index.js to dist/main.js
npm run build
// src/index.js
import { camelCase } from "lodash";
console.log(camelCase("hello world"));
<!DOCTYPE html>
<html lang="en">
<head>
<!-- ... -->
</head>
<body>
<!-- <script src="../src/index.js"></script> -->
<script src="../dist/main.js"></script>
</body>
</html>
모듈 번들러의 종류로는 webpack, roll up, parcel, snowpack이 있지만 그 중 가장 많이 사용되는 것은 webpack입니다.
// Customize behavior of webpack
// webpack.config.js
const path = require("path"); // Consistent path name
const BundleAnalyzerPlugin =
require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
module.exports = {
// Entry Points
entry: "src/index.js", // Entry point
entry: {
foo: "foo.js", // Entry object(code splitting)
bar: "bar.js",
},
// Output
output: {
filename: "main.js", // Filename to compile
path: path.resolve(__dirname, "dist"), // File location
},
// Loaders
module: {
// Match files to loaders(css, style, sass-loader)
rules: [
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
],
},
// Plugins
// webpack-bundle-analyzer
plugins: [new BundleAnalyzerPlugin()],
// Dev Server(watch and serve files)
// webpack-dev-server
devServer: {
contentBase: path.join(__dirname, "public"),
port: 9000,
},
};
Module Bundlers Explained... Webpack, Rollup, Parcel, and Snowpack