대부분의 언어들은 모듈 시스템을 지원하고 있는데요.
c에서는 헤더 파일을 통해, java에선 패키지 시스템, python은 모듈과 패키지를 사용하는 등 언어 자체적으로 모듈 시스템을 지원하여 코드 재사용성을 높이고 있습니다.
그렇다면 자바스크립트에서는 어떨까요?
const module = require("some-package")
import module from 'some-package'
위와 같은 표현을 많이 만나 보셨을 텐데요. 그렇습니다, 자바스크립트에서도 모듈 시스템을 지원하고 있습니다.
하지만 자바스크립트의 모듈 시스템은 언어가 처음 개발되었을 때에는 존재하지 않았는데요.
자바스크립트는 처음에는 웹 브라우저의 동작을 제어하는 간단한 스크립팅 언어로 개발되었기 때문입니다.
자바스크립트의 공식적인 모듈 시스템은 ES6 (ECMAScript 2015)에 추가되어 import와 export 문법을 이용하여 모듈을 정의하고 가져올 수 있게 되었습니다.
ESM이 발표되기 전에는 어떤 방법으로 모듈 시스템 부재를 극복할 수 있었을까요?
다양한 방법이 존재하지만 그 중 하나로 모든 자바스크립트 파일을 하나로 묶어서 하나의 자바스크립트 파일로 만드는 방법이 있습니다. 이러한 번들링을 도와 주는 도구 중 가장 널리 사용되는 것이 Webpack 입니다.
Webpack을 사용하여 많은 자바스크립트 파일을 하나로 번들링 하여 브라우저 로딩 속도가 얼마나 감소되는지 파악해 보는 것이 이번 포스팅의 주제입니다.
간단한 html과 Live Server를 이용하여 테스트를 진행해 보겠습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script type="module" src="./index.js"></script>
</head>
<body>
<button type="button" id="buttonA">buttonA</button>
<button type="button" id="buttonB">buttonB</button>
<button type="button" id="buttonC">buttonC</button>
<div id="display"></div>
</body>
</html>
import a from './a.js';
import b from './b.js';
import c from './c.js';
const buttonA = document.querySelector('#buttonA');
const buttonB = document.querySelector('#buttonB');
const buttonC = document.querySelector('#buttonC');
const display = document.querySelector('#display');
buttonA.addEventListener('click', () => {
display.textContent = a;
});
buttonB.addEventListener('click', () => {
display.textContent = b;
});
buttonC.addEventListener('click', () => {
display.textContent = c;
});
import a1 from './a-1.js';
export default `this is ${a1}`;
export default 'module a-1';
이제 브라우저에서 로딩 속도를 확인해 볼게요.
모든 자바스크립트 파일이 로드 되기까지 292ms가 소요되었습니다. 그렇다면 이제 웹팩을 이용하여 자바스크립트 파일을 하나로 번들링 해 볼까요?
우선 devDependencies에 webpack과 webpack-cli를 추가해 주세요
{
"name": "webpack",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"webpack": "^5.84.1",
"webpack-cli": "^5.1.1"
}
}
그리고 다음 커맨드를 통해 번들링을 실행합니다.
npx webpack --entry ./index.js --output-path ./dist --mode production
Webpack으로 번들링 할 진입점(entry)파일을 index.js로 지정하고, 번들링 된 파일의 출력 경로를 지정합니다. 이를 통해 minified된 하나의 자바스크립트 파일을 얻을 수 있습니다.
(()=>{"use strict";const t=document.querySelector("#buttonA"),e=document.querySelector("#buttonB"),n=document.querySelector("#buttonC"),o=document.querySelector("#display");t.addEventListener("click",(()=>{o.textContent="this is module a-1"})),e.addEventListener("click",(()=>{o.textContent="this is module b-1"})),n.addEventListener("click",(()=>{o.textContent="this is module c-1"}))})();
파일을 살펴보면 버튼에 대한 event listner가 정의 되어 있네요. 신기한 점은 a-1.js에서 가져온 값을 따로 변수에 할당하지 않고 다음과 같이 값이 사용되는 곳에 바로 적용된다는 것입니다.
()=>{o.textContent="this is module a-1"}
그럼 번들링 된 파일을 script 태그에서 불러와 브라우저에서 로드되기 까지의 시간을 알아볼까요?
main.js 파일 하나만 가져오고, 소요 시간은 31ms로 기존에 번들링 하기 전보다 10배 가량 단축 되었습니다.
Webpack이 import 구문을 사용하는 파일을 처리하는 내부적인 과정을 더 구체적으로 알아볼까요?
// Before transformation
import { myFunction } from './myModule';
// After transformation
var _myModule = __webpack_require__("./myModule");
var myFunction = _myModule.myFunction;
하지만 이러한 번들링이 무조건적으로 웹 사이트의 초기 로딩을 줄이는데 도움이 된다고 말할수 있을까요?
정답은 X 입니다. 번들링의 결과로 생성된 파일의 크기가 너무 크면, 단일 HTTP 요청의 지연시간이 오히려 더 커질 수 있는데요.
이러한 이유로 Webpack에는 code splitting 기능이 내장되어 있습니다. 이 기능을 사용하면 큰 번들을 여러개의 작은 번들로 나눌 수 있기 때문에, 사용자가 실제로 필요한 코드만 초기에 로드할 수 있어, 로딩 시간을 줄일 수 있습니다.
결론적으로, Webpack을 사용하는 것이 웹사이트의 성능을 향상시키는 데 도움이 될 수 있지만,
단순히 모든 코드를 하나의 큰 번들로 만드는 것만으로는 충분하지 않습니다.
코드 분할, 지연 로딩, 미니화 등의 전략을 사용해야 효과적으로 웹사이트의 로딩 시간을 줄일 수 있습니다.
이상으로 Webpack 번들링의 효과: 로딩 속도 비교 포스팅을 마치겠습니다. 감사합니다.