번들러를 알기 위한 최소한의 지식
모듈
https://ko.javascript.info/modules-intro#ref-725
모듈 : 분리된 JS 파일 각각을 모듈(module)이라 한다.
기존 : CJS, AMD, => UMD
모던 JS, ES6 버전 이후로, 새로운 ES모듈 방식 통일 되고 있음
node.js에서 흔히 본 require 구문이 CJS 방식이다.
- CommonJS – Node.js 서버를 위해 만들어진 모듈 시스템입니다.
// math.js
module.exports = {
add: function(a, b) {
return a + b;
}
};
// app.js
const math = require('./math.js');
console.log(math.add(1, 2)); // 3
- ( AMD – 가장 오래된 모듈 시스템 중 하나, require.js라는 라이브러리를 통해 개발 )
- define 및 require 구문 사용
// math.js
define(function() {
return {
add: function(a, b) {
return a + b;
}
};
});
// app.js
require(['math'], function(math) {
console.log(math.add(1, 2)); // 3
});
- ( UMD – AMD와 CommonJS와 같은 다양한 모듈 시스템을 함께 사용하기 위해 만들어졌습니다. )
- AMD(브라우저 지원) + CJS(node.js 지원) 모두 지원하도록 분기처리하면 UMD 모듈
// math.js
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory();
} else {
// Browser globals (root is window)
root.returnExports = factory();
}
}(typeof self !== 'undefined' ? self : this, function () {
return {
add: function(a, b) {
return a + b;
}
};
}));
// app.js (CommonJS example)
const math = require('./math.js');
console.log(math.add(1, 2)); // 3
ES Module, EJS
// 📁 sayHi.js
export function sayHi(user) {
alert(`Hello, ${user}!`);
}
// 📁 main.js
import {sayHi} from './sayHi.js';
alert(sayHi); // 함수
sayHi('John'); // Hello, John!
// index.html
<!doctype html>
<script type="module">
import {sayHi} from './say.js';
document.body.innerHTML = sayHi('John');
</script>
모듈은 일반스크립트와 차이점이 있다.
엄격 모드로 실행 됨
- 선언되지 않은 변수에 값을 할당하는 등의 코드는 에러를 발생
'use strict'
모듈 레벨 스코프
- 모듈은 자신만의 스코프 존재, 따라서 모듈 내부에서 정의한 변수나 함수는 다른 스크립트에서 접근 불가.
- 브라우저 환경에서도
<script type="module">
을 사용해 모듈을 만들면 독립적인 스코프가 만들어진다. - 브라우저 환경에서 부득이하게 window 레벨 전역 변수를 만들어야 한다면, window 객체에 변수를 명시적으로 할당하여 가능
단 한 번만 평가됨
- alert 함수가 있는 모듈(alert.js)을 여러 모듈에서 가져와도, 한번만 실행된다.
- 실무에서 모듈 초기화, 이를 재사용 하는데 활용 가능
- 예시) admin.js의 name이 null값 > init.js로 초기화 하여 name을 주입 > other.js에서 name값을 사용할 수 있음.
import.meta
- import.meta 객체는 현재 모듈에 대한 정보를 제공
- 브라우저 환경에선 스크립트의 URL 정보
this는 undefined
- 모듈 최상위 레벨의 this는 undefined입니다.
<script>
alert(this); // window
</script>
<script type="module">
alert(this); // undefined
</script>
모듈의 브라우저 특정 기능
지연 실행
- defer 속성을 붙인 것처럼 실행,
<script defer src="" />
- 브라우저의 HTML 처리가 멈추지 않고, 모듈과 리소스를 병렬적으로 불러온다.
- HTML보다 먼저 로딩 되어도, HTML 처리 후 실행된다. 완전한 HTML파일에 접근 가능
- 스크립트의 순서가 유지 된다.
인라인 스크립트의 비동기 처리
- *인라인 스크립트 : html안에 script 태그를 넣는 방식
- 일반 스크립트에서 async 속성은 외부 스크립트를 불러올 때만 유효, 하지만 모듈은 인라인 스크립트도 적용 가능.
- async속성은 로딩이 끝나면 다른 스크립트나 HTML 문서가 처리되길 기다리지 않고 바로 실행
외부 스크립트
- 1.src 속성값이 동일한 외부 스크립트는 한 번만 실행됩니다.
- 2.외부 사이트같이 다른 오리진에서 모듈 스크립트를 불러오려면 CORS 헤더가 필요합니다.
- 모듈이 저장되어있는 원격 서버가 Access-Control-Allow-Origin: * 헤더를 제공
빌드 툴
- 웹팩(Webpack)과 같은 특별한 툴을 사용해 모듈을 한 데 묶어(번들링) 후 프로덕션 서버에 올린다.
- CSS, HTML 포맷의 모듈을 사용할 수 있게 해준다는 장점이 있습니다.
빌드 툴의 역할
- 1.HTML의
<script type="module">
에 넣을 ‘주요(main)’ 모듈(‘진입점’ 역할을 하는 모듈)을 선택합니다. - 2.‘주요’ 모듈에 의존하고 있는 모듈 분석을 시작으로 모듈 간의 의존 관계를 파악.
- 3.모듈 전체를 한데 모아 하나의 큰 파일을 만듭니다 (코드 스플릿 가능).
- 이 과정에서 import문이 번들러 내 함수로 대체되므로 기존 기능은 그대로 유지됩니다.
- 4.이런 과정 중에 변형이나 최적화도 함께 수행됩니다.
- 도달 가능하지 않은 코드는 삭제
- 내보내진 모듈 중 쓰임처가 없는 모듈을 삭제 (tree-shaking)
- console, debugger 같은 개발 관련 코드를 삭제.
- 최신 자바스크립트 문법이 사용된 경우 바벨(Babel)을 사용해 동일한 기능을 하는 낮은 버전의 스크립트로 변환합니다.
- 공백 제거, 변수 이름 줄이기 등으로 산출물의 크기 최소화
번들링 툴을 사용하면 스크립트들은 하나 혹은 여러 개의 파일로 번들링 됩니다.
- 이때 번들링 전 스크립트에 있던 import, export문은 특별한 번들러 함수로 대체됩니다.
- 번들링 과정이 끝나면 기존 스크립트에서 import, export가 사라지기 때문에 type="module"이 필요 없어집니다.
- 따라서 아래와 같이 번들링 과정을 거친 스크립트는 일반 스크립트처럼 취급할 수 있습니다.
<!-- 웹팩과 같은 툴로 번들링 과정을 거친 스크립트인 bundle.js -->
<script src="bundle.js"></script>