브라우저에서 ESM(ES Modules)을 지원하기 전까지, JavaScript 모듈화를 네이티브 레벨에서 진행할 수 없었다. 그래서 소스 모듈을 브라우저에서 실행할 수 있는 파일로 크롤링, 처리 및 연결하는 Bundling 번들링이라는 해결 방법을 사용해야 했었다.
크롤링 Crawling
번들링 Bundling
<html>
<script src="/src/foo.js"></script>
<script src="/src/bar.js"></script>
<script src="/src/baz.js"></script>
</html>
// foo.js, bar.js, baz.js 중 하나라도 변수 이름이 겹치는 선언이 전역에 있다면 충돌이 발생한다.
이전에는 CommonJS, AMD(Asynchronous Module Definition), UMD(Universal Module Definition) 등의 모듈 시스템이 있었지만, 이들은 언어 자체에 내장되어 있지 않아서 브라우저에서 바로 사용할 수 없었다.
2008년에 구글에서 브라우저 외부에서도 JavaScript를 실행시킬 수 있는 V8엔진을 소개하면서 모듈화에 대한 필요성이 더욱 부각됐다. 브라우저 외부 환경에서 JS를 실행할 수 있게 되면서, 특히 Node.js를 통해 JavaScript로 서버를 개발하는 것이 큰 인기를 얻게 됐다. 더불어 모듈 표준화를 위한 움직임이 본격적으로 시작됐다.
EcmaScript 6 (ES6)에서 도입된 기능. 브라우저와 Node.js에서 네이티브로 JavaScript 모듈 시스템을 지원한다.
<!-- 모듈을 포함하는 HTML 파일 -->
<script type="module">
// 모듈 로드
import { greet } from './greeting.js';
// 모듈의 함수 사용
greet('John');
</script>
// greeting.js 파일
export function greet(name) {
console.log(`Hello, ${name}!`);
}
모든 브라우저에서 모듈 시스템을 완전하게 지원하지 않았기 때문에 개발자들은 '번들링'이라는 우회적인 방법을 사용해야 했다. 따라서 브라우저와 무관하게 모듈 시스템을 지원하기 위해 Webpack
, Rollup
그리고 Parcel
같은 번들링 도구, 번들러가 탄생했다. 이런 번들러 도구는 프런트엔드 개발자의 생산성을 크게 향상시켜줬다.
기존 번들러들은 엔트리 JavaScript 파일부터 시작해서 소스 코드와 node_modules
폴더 전체 코드 베이스를 묶고, 필드 프로세스를 통해 실행한 다음 번들된 코드를 브라우저에 제공한다.
콜드 스타트 방식(최초로 실행되어 이전에 캐싱한 데이터가 없는 경우를 의미)으로 개발 서버를 구동할 때, 번들러 기반의 도구의 경우 애플리케이션 내 모든 소스 코드에 대해 크롤링 및 빌드 작업을 마쳐야지만이 실제 페이지를 제공할 수 있다.
웹 애플리케이션이 커지면 커질수록 애플리케이션을 시작하는데 오래 걸리게 되었고, 또한 대부분의 번들러는 Node.js 기반으로 돌아가기 때문에 싱글 스레드로 인한 처리 한계를 가졌다.
위에서 나타난 문제점을 해결하기 위해 snowpack, vite 같은 차세대 번들러들이 등장했다.
Vite는 애플리케이션의 모듈을 Dependencies와 Source code 두 가지 카테고리로 나누어 개발 서버의 시작 시간을 개선했다.
위에 초록색 상자로 표시된 route를 어떤 페이지라고 해보자. 이 페이지를 요청하면 브라우저는 route에 해당하는 브랜치의 정보만 필요하다. 따라서 브라우저는 ESM을 통해 이 페이지의 모듈에 대한 소스 코드만을 번들러에게 요청하고, 번들러는 해당하는 소스 코드만을 전달한다.
어떤 모듈이 수정됐을 때도 번들러는 수정된 모듈과 관련된 부분만 교체하고, 브라우저에서 해당 모듈을 요청하면 교체된 모듈을 전달하면 되기 때문에 전체를 다시 번들할 필요가 없다. 전 과정에서 완벽하게 ESM을 이용하기에, 앱 사이즈가 커져도 HMR을 포함한 갱신 시간에는 영향을 끼치지 않는다.
여기서 더 나아가 vite의 경우 HTTP 헤더를 이용해 요청한 소스 코드가 304 Not Modified
, 디펜던시는 Cache-Control: max-age=31536000
, immutable
를 이용해 캐시되도록 함으로써 요청을 줄여 성능을 더 높였다.
Vitest를 사용하기 위해서 Vite를 자연스레 설치하게 되었는데 (Webpack 사용시에도 물론 쓸 수 있음.) 나는 번들러를 써본 적이 없어서 Vite도 써보고 싶었다. 들어보기만한 것들에 대해 더욱 잘 알게되었다.
요즘 기술선택때문에 vite랑 webpack..에대해서 저울질하고 있는데 잘 주어담고 갑니다. 👍