웹 사이트의 규모↑ 프로그래밍 소스를 관리하고 배포하는 비용↑ 레거시 코드의 의존성 파악 어려움↑
따라서, JavaScript를 범용적으로 사용하기 위해서는 ‘모듈화’ 필수! (이러한 모듈화 작업 때문에 Node.js 탄생)
모듈화 시스템 등장 이전에는 브라우저에서 <script>
태그를 사용하여 파일 불러오는 작업 실행
but 전역 스코프를 공유하는 치명적인 단점이 존재한다.
<!DOCTYPE HTML>
<html lang="ko">
<head>
<title>MODULE</title>
<script src="a.js"></script> <!-- let number = 10 -->
<script src="b.js"></script> <!-- let number = 20 -->
<script>
console.log(number); // number = 20
</script>
</head>
</html>
모듈화(Modularization)
재사용 가능한 코드의 덩어리, 어떤 기능에 대한 코드 묶음 (= 하나의 파일!)
모듈화 3조건 : 1. 스코프 (Scope) 2. 정의 (Definition) 3. 사용 (Useage)
AMD
: 가장 오래된 모듈 시스템 중 하나 (RequireJS)CommonJS
: Node.js 환경을 위해 만들어진 모듈 시스템UMD
: AMD와 CommonJS 등 다양한 모듈 시스템을 함께 사용하기 위해 개발ES Module
: ES2015(ES6)에서 도입된 모듈 시스템<script type="module">
export
, import
JavaScript가 브라우저에 종속된 언어에서 벗어나 서버에서도 사용 가능한 언어가 되면서 Node.js 환경에서 모듈을 사용하기 위해 채택된 모듈 시스템으로, 동기적 특성을 가져 브라우저와 서버 사이드 개발에 모두 사용하기 용이얃야얃
exports
module.exports
require
const mod1Function = () => console.log('첫번째 모듈화!')
const mod1Function2 = () => console.log('두번째 모듈화!')
module.exports = { mod1Function, mod1Function2 }
exports.mod1Function = () => console.log('첫번째 모듈화!')
exports.mod1Function2 = () => console.log('두번째 모듈화!')
const mod1Function = require('./module.js')
const mod1Function2 = require('./module.js')
const testFunction = () => {
console.log('CommonJS를 학습합니다.')
mod1Function()
}
testFunction()
🎓 Quiz!
let a = 1;
let b = 2;
exports.sum = function(c, d) {
return a + b + c + d;
}
let a = 3;
let b = 4;
const moduleA = require("./A");
moduleA.sum(a, b); // ?
10
CommonJS의 모듈 명세는 모든 파일이 로컬 디스크에 저장되어 있어 필요할 때 바로 불러올 수 있는 상황을 전제한다. 즉, 서버 사이드 환경(Node.js)이 전제 조건으로 설정되어 있다. 하지만 이러한 방식은 필요한 모듈을 모두 내려받을 때 까지 사용 불가한 결정적인 단점을 가지고 있다.
<script>
태그 삽입하는 방식 사용 (JavaScript loader가 사용하는 일반적인 방법)브라우저에서는 서버 사이드 환경과 달리 파일 단위의 스코프가 존재하지 않는다. 또한 <script>
태그를 이용해 파일을 불러올 경우 순서에 따라 전역 변수가 겹치는 문제가 발생한다. 때문에 서버 모듈을 비동기적으로 클라이언트에 전송하기 위해서는 해결책이 필요!
모듈 전송 포맷(Module Transport Format)
추가 정의 이 명세에 따라 서버 사이드에서 사용하는 모듈을 브라우저에서 사용하는 모듈과 같이 전송 포맷으로 감싸주면 비동기적으로 로드가 가능하다.서버 사이드에서 사용하는 모듈
let sum = require("./math").sum;
exports.plusTwo = function(a) {
return sum(a, 2);
};
plus-two.js
에서 같은 위치에 있는 math.js
내의 sum
함수를 require
(불러오기)
이후 plusTwo
라는 함수를 만들어 exports
(내보내기)
브라우저에서 사용하는 모듈
require.define({"complex-numbers/plus-two": function(require, exports) {
let sum = require("./complex-number").sum;
exports.plusTwo = function(a) {
return sum(a, 2);
};
}, ["complex-numbers/math"]);
브라우저에서는 똑같은 상황임에도 ["complex-numbers/math"]
를 통해 먼저 로드되어야 할 모듈을 기술하고 require.define
안에 plus-two
라는 모듈을 콜백 함수 안에 정의
= 비동기적 모듈 선언
CommonJS가 서버 사이드 환경에서 사용하기 좋은 반면 AMD는 필요한 파일을 네트워크를 통해 내려받아야 하는 브라우저 환경에 적합하다.
define()
define('모듈이름', ['의존성1', '의존성2'], function( 의존성1, 의존성2 ){
// 모듈 코드
})
require()
require(['모듈이름'], function(모듈) {
// 모듈 사용
})
브라우저 환경은 파일 스코프가 존재하지 않기 때문에 define()
함수로 파일 스코프 처리 (네임스페이스 역할)
define(id?, dependencies?, factory)
id
: 모듈을 식별할 때 사용하는 인수. id가 없으면 로더가 요청하는
dependencies
: 정의하려는 모듈의 의존성을 나타내는 배열 (먼저 로드해야 하는 모듈)factory
: 모듈이나 객체를 인스턴스화 하는 실제 구현 담당// define을 이용해 새로운 모듈을 불러오고, 콜백함수로 전해줌
define(['package/lib'], function (lib) {
// 콜백함수 이용해서, 불러온 모듈 사용가능
function foo () {
lib.log('hello world!');
}
// 다른파일에서 foo함수를, foobar이란 이름의 모듈로 불러올 수 있게 만듬
return {
foobar: foo
};
});
// 위에서 선언한 모듈 불러오기
require(['package/myModule'], function (myModule) {
myModule.foobar();
});
AMD와 CommonJS 두 그룹으로 나눠지다 보니 서로 호환되지 않는 문제가 발생한하는데,
이를 해결하기 위해 나온 방법으로 AMD와 CommonJS, window에 추가하는 방식까지 모두 가능하다.
// root: 전역범위, factory: 모듈을 선언하는 함수
(function (root, factory) {
// AMD 방식
if (typeof define === 'function' && define.amd) {
define(['exports', 'b'], factory);
// CommonJS 방식
} else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
factory(exports, require('b'));
// Browser globals (window 객체에 모듈 내보냄)
} else {
factory((root.commonJsStrict = {}), root.b);
}
}(this, function (exports, b) { // 모듈을 생성하는 익명 함수
exports.action = function () {};
}));
현재 브라우저는 ES6 문법에 따라 ES Module
사용, Node.js 환경에서는 CommonJS
를 기반으로 사용
Node.js 환경에서 ES Module
사용을 위해서는 Babel
과 같은 트랜스 컴파일러 도구를 사용해야 했지만,
Node.js v13.2 부터 ES Module
정식 지원이 시작됨에 따라 CommonJS
, ES Module
함께 사용 가능
{
"name": "node",
"version": "1.0.0",
"description": "",
"main": "b.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"type": "module" // 여기!
}