모듈은 1개 이상의 값
을 내보내는 JS파일이다.
모듈을 통해 기능과 관심사의 분리
를 할 수 있다.
자바스크립트 모듈 선언 포맷인 AMD
와 CommonJS
,
브라우저와 노드에서 모두 사용가능하도록 만든 UMD
,
브라우저에서 동작하는 모듈 기능인 ESM
에 대해 알아본다.
모듈을 선언하면서 의존하고 있는 모듈도 함께 명시한다.
필요에 따라 비동기적으로 의존모듈을 불러온다.
실행할 코드가 필요한 파일만 가져오므로 성능이 좋다.
의존모듈을 불러온 후, 코드를 실행할 수 있어 오류가 줄어든다.
AMD 포맷으로 구현한 라이브러리에는 Require.js
, Dojo Toolkit
이 있다.
// define(의존모듈 배열로 선언, function(){})
define(['jQuery'], function($){
function withjQuery(){}; // 함수선언
return withjQuery; // 외부에 함수 노출
}
브라우저가 아닌 환경에서 js 모듈 포맷을 확립했다.
서로다른 개발환경(서버, desktop, app)에서 만든 js코드를 공유하기 위해
2009년 serverJS 프로젝트로 시작했고, CJS로 이름 변경됨
Node.js 에서 구현되어 있다.
// 의존모듈 선언
var $ = require('jQuery');
// 함수 선언
function withjQuery(){};
// 외부에 함수 노출
module.exports= withjQuery;
노드와 브라우저 환경 모두를 지원하도록 만든 것이 UMD 이다.
UMD는 코드작성 패턴으로 볼 수 있는데 레포지토리에 가보면
여러가지 템플릿 코드를 확인할 수 있다.
returnExports.js 파일의 예시 코드를 보면
define을 지원하면 AMD 형식으로
module을 지원하면 CJS 형식으로
그 이외라면 브라우저로 전역객체에 지정하는 방식으로 작성함을 알 수 있다.
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// CommonJS
module.exports = factory();
} else {
// browser
root.isDev = factory();
}
})(this, function() {
return process.env.NODE_ENV === 'development';
});
코드 설명 추가
// 모듈 선언을 위해 IIFE 패턴을 사용하고 있다.
// 이 예제에서는 의존 모듈을 표현하기 위해 임의로 모듈 'b'를 사용한다
// root 파라미터는 실행 환경에 따라 브라우저의 window 또는 node.js의 global을 가리킨다.
// factory 파라미터는 모듈 코드를 감싸고 있는 함수를 가리킨다.
(function (root, factory) {
// AMD에서 사용하는 define 함수를 사용할 수 있는지 확인한다.
if (typeof define === 'function' && define.amd) {
// AMD 포맷에 따라 모듈을 선언한다. 의존 모듈은 b가 된다.
define(['b'], factory);
// 의존 모듈이 없다면 아래와 같이 작성한다
// define([], factory);
// AMD를 지원하지 않는다면 CommonJS 모듈을 사용할 수 있는지 확인한다.
} else if (typeof module === 'object' && module.exports) {
// CommonJS 포맷에 따라 모듈을 선언한다.
// 의존 모듈을 factory 함수의 파라미터로 전달해준다.
module.exports = factory(require('b'));
// 의존 모듈이 없다면 아래와 같이 작성한다
// module.exports = factory();
} else {
// AMD도 CommonJS도 아니라면 브라우저라고 판단한다.
// 여기서 root는 window가 된다. returnExports는 전역에서 모듈의 이름이다.
root.returnExports = factory(root.b);
// 의존 모듈이 없다면 아래와 같이 작성한다
// root.returnExports = factory();
}
// 함수를 즉시 실행한다.
// 첫번째 파라미터는 전역 변수.
// (여기서는 Web Worker 지원을 위해 self 변수를 확인하지만 그럴 필요가 없다면 this만 전달해도 된다)
// 두번째 파라미터는 factory에 해당한다.
}(typeof self !== 'undefined' ? self : this, function (b) { // 의존 모듈 b
// return하는 값이 이 모듈이 내보내는 값이 된다(이 예제에서는 객체).
return {};
}));
ES6에서 client-side js에서 동작하는 모듈이 추가되었다.
script 태그에 type="module"
속성을 추가하면 js파일이 모듈로 동작한다.
브라우저에서는 태그의 속성을 보고 모듈로 구문분석을 하게 되지만,
노드환경에서는 알 수 없기 때문에, 파일확장자로 .mjs
사용을 권장한다.
ES6 모듈은 독자적인 모듈 스코프를 갖는다.
export
키워드를 사용하여 외부에 공개할 수 있다.
export const pi = 3.141592
// 객체로 공개
export { pi, square, Person }
// export default 를 사용할 경우 var, let, const 키워드 사용 불가
export default x => x * x;
// ESM에서는 파일 확장자를 생략할 수 없다.
import { pi, square, Person } from './lib.mjs'
// lib.pi 로 사용 가능
import * as lib from './lib.mjs'
// as 사용하여 이름 변경
import { pi as PI, square as sq } from './lib.mjs'