module-bundler
Module Bundler
는 프론트엔드 개발자들이 주로 사용하며 JavaScript 모듈을 브라우저에서 실행할 수 있는 단일 JavaScript 파일로 묶는 데 사용되는 도구이다.
최신 모듈 번들러 예시
- webpack
- rollup
- fusebox
- parcel
다음과 같은 이유로 Module Bundler
를 필요로 할 수 있다.
- 브라우저는 모듈 시스템을 지원하지 않지만 오늘날에는 완전히 사실이 아닙니다.
- 코드의 종속성 관계를 관리하는 데 도움이 되며 종속성 순서로 모듈을 로드합니다.
- 종속성 순서, 이미지 자산, CSS 자산 등으로 자산을 로드하는 데 도움이 됩니다.
Module?
Module
이란 프로그래밍 관점에서 특정 기능을 갖는 작은 코드 단위를 의미합니다.
JavaScript의 Simple Example
1 | // math.js |
이 math.js 파일은 아래와 같이 3가지 기능을 갖고 있는 Module
이다.
- 두 숫자의 합을 구하는 sum() 함수
- 두 숫자의 차를 구하는 substract() 함수
- 원주율 값을 갖는 pi 상수
이처럼 성격이 비슷한 기능들을 하나의 의미 있는 파일로 관리하면 Module
로 취급된다.
Why Need Bundler
설명을 위해 여러 JavaScript 파일로 구성된 웹 응용 프로그램을 구축하는 상황을 가정해보자
아래는 필요한 JavaScript 파일을 html에 추가하는 예시
1 | <html> |
각 파일에는 5개의 왕복 요청인 별도의 http 요청이 필요하다.
따라서 5개의 파일을 모두 하나로 결합할 수 있다면 더 효율적이다.
1 | <html> |
Bundle Problem
- 포함될 “파일” 의 순서를 어떻게 유지?
- “파일” 사이에 일종의 종속성 순서가 있으면 좋음
- “파일” 간의 이름 충돌을 방지하려면?
- 번들 내에서 사용되지 않은 “파일”을 어떻게 확인?
다음과 같이 각 파일 간의 관계를 알면 이 모든 것을 해결할 수 있습니다.
- 어떤 파일이 다른 파일에 종속되어 있는지?
- 파일에서 노출되는 인터페이스는 무엇?
- 어떤 노출된 인터페이스가 다른 사람에 의해 사용되고 있습니까?
부여된 이러한 정보는 각각 제기된 문제를 해결할 수 있다.
따라서 우리에게 필요한 것은 파일 간의 관계를 설명하는 선언적 방법이며, 이는 우리를 JavaScript 모듈 시스템으로 이끈다.
CommonJS & ES6 Module
CommonJS 또는 ES6 모듈은 우리가 의존하고 있는 파일과 파일에서 사용 중인 인터페이스를 지정할 수 있는 방법을 제공한다.
1 | // CommonJS |
How Bundle?
모듈 시스템에서 수집한 정보로 어떻게 파일을 함께 연결하고 모든 것을 캡슐화하는 번들 파일을 생성할까?
대표적인 2개의 Module Bundler를 사용
- webpack
- rollup
우리는 3개의 파일을 가지고 있다고 가정해보자
- circle.js
- square.js
- app.js
1 | // filename: circle.js |
“웹팩 방식”
“webpack 방식” 번들은 어떻게 생겼을까?
1 | // filename: webpack-bundle.js |
첫째, 가장 먼저 눈에 띄는 것은
Module Map
이다. 모듈 이름을 함수로 래핑된 모듈 자체에 매핑하는 사전입니다.Module Map
은 항목을 추가하여 모듈을 쉽게 등록할 수 있다.둘째, 각 모듈은 함수로 Wrapping 된다. 이 함수는 모듈 내에서 선언된 모든 것이 자체 내에서 범위가 지정되는 모듈 범위를 시뮬레이션합니다. 함수 자체를
Module Factory Function
라고 합니다. 모듈이 인터페이스를 내보내고 다른 모듈에서 요구할 수 있도록 몇 가지 매개 변수를 사용한다.셋째, 어플리케이션이 시작되고
webpackStart
는 모든 것을 함께 붙여주는 함수이다. 종종런타임
이라고 하는 함수 자체는 번들에서 가장 중요한 부분이다.모듈 맵
과입력 모듈
을 사용하여 애플리케이션을 시작한다.
1 | // filename: webpack-bundle.js |
webpackStart
require function
과 module cache
의 2가지를 정의한다.
require
기능은 CommonJS의 기능과 다르다.
require
구문은 exported interface
를 module로 부터 반환한다.
예시: circle.js 라면 -> { default: function area(radius){ ... } }
반환
내보낸 인터페이스는 모듈 캐시에 캐시되므로 동일한 모듈 이름의 require
를 반복적으로 호출하면 모듈 팩토리 함수
가 한 번만 실행됩니다.
require
가 정의된 상태에서 애플리케이션을 시작하는 것은 입력 모듈을 “요구”하는 것뿐입니다.
“롤업 방식”
이제 “롤업 방식” 번들을 살펴보자.
1 | // filename: rollup-bundle.js |
- 첫째, 롤업 번들의 주요 차이점은 웹팩 번들에 비해 훨씬 작다. “webpack 방식”에 비해 모듈 맵 이 없으며, 모든 모듈은 번들로 “평평하게” 정의된다. 모듈의 래핑
function
이 없다. 모듈 내에서 선언된 모든 변수/함수는 이제 전역 범위로 선언된다.
혹시 개별 모듈 범위에서 선언된 모든 것이 전역 범위로 선언된 경우 2개의 모듈이 동일한 이름의 변수/함수를 선언한다면?
- 롤업은 이름 충돌이 발생하지 않도록 변수/함수 이름을 변경한다.
- 예시에서
circle.js and square.js
는 모두function area(){}
모듈 내에서 선언 되었으며 번들될 때 충돌을 피하기 위해 두 함수와 그 사용법이 모두 이름이 변경된 것을 볼 수 있다.
모듈을 함수로 래핑하지 않는 부작용 중 하나의 동작이다
eval
. 자세한 설명 은 문서를 참조
- 둘째, 번들 내 모듈의 순서가 문제가 있을 수 있다.
circle$area
->square$area
->console.log
순서대로 작동하지만 temporal dead zone 때문에 PI전에 선언해야한다.
따라서 종속성 순서대로 모듈을 정렬하는 것은 “롤업 방식”에 중요하다.
대체로 “롤업 방식”이 “웹팩 방식”보다 나은 것 같아보인다. 모든 기능을 제거하여 더 작은 번들과 더 적은 런타임 오버헤드를 갖는 특징이 있다.
요약
- 모듈 번들러 는 여러 JavaScript 모듈을 하나의 JavaScript 파일로 결합하는 데 큰 도움이 된다.
- “웹팩 방식”
- 모듈 맵 사용
- 함수를 사용하여 각 모듈을 래핑
- 모듈을 함께 연결하는 런타임 코드가 있음
- “롤업 방식”
- 더 평평하고 작은 묶음
- 모듈을 래핑하기 위해 함수를 사용하지 않음
- 순서 문제, 종속성을 기반으로 한 정렬 필요
- 순환 종속성이 작동하지 않을 수 있음