모듈은 관련된 기능을 하나로 묶어 다른 코드와 결합도를 줄이고, 재사용성을 높이기 위해 사용된다.

ES5의 모듈 패턴

  • 즉시 실행 함수 표현식(IIFE, Immediately Invoked Function Expressions)
    • IIFE 내부의 코드 복잡도를 캡슐화하여 IIFE 코드가 무엇을 하는지 이해하지 않아도 된다.
    • IIFE 안에 변수를 정의하여 전역 스코프를 오염시키지 않는다. (IIFE 내부의 var문은 IIFE의 클로저 안에 남아있는다)
1
2
3
4
// 함수표현식은 함수를 반환하고, 즉시 이 함수를 호출할 수 있다.
(function(){
console.log('test');
})()

하지만 IIFE는 의존성 관리를 위한 메커니즘은 제공하지 않는다.

  • 노출식 모듈 패턴
1
2
3
4
5
6
7
8
9
10
11
12
var module = function(){ 
function sayHello(){
console.log('Hello');
}
// Expose API
return {
sayHello: sayHello
}
}()

// 변수를 통해서 접근 가능
module.sayHello();

노출식 모듈 패턴 또한 의존성 관리 메커니즘은 제공하지 않는다.

따라서..

모듈화 시키는데 대표적인 작업 그룹이 있었으니 CommonJsAMD(Asynchronous Module Definition)가 있다.

AMD(Asynchronous Module Definition)

브라우저 환경을 중심으로 설계된 규약으로, 필요한 모듈을 네트워크를 통해 내려받을 수 있도록 하는 비동기 모듈을 표준안으로 다루고 있다. 브라우저 환경의 JavaScript는 파일 스코프가 따로 존재하지 않기 때문에 이 define() 함수로 파일 스코프의 역할을 대신한다.

define() 함수는 전역함수로 AMD 명세를 구현하는 모듈로더를 통해 실행시켜야 한다.

define(모듈 식별 id, 먼저 로드되야 하는 모듈, 팩토리 함수);

1
2
3
4
5
6
7
8
9
10
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {  
exports.verb = function() {
// 넘겨받는 인수를 사용해도 되고
return beta.verb();

// 또는 require()를 이용해
// 얻어 온 모듈을 사용해도 된다.
return require("beta").verb();
}
});

AMD 모듈 명세의 장점은 브라우저/서버사이드에서 동일한 코드로 동작한다는 점이다. define()함수를 통해 전역변수 문제를 해결하며, 모듈을 필요한 시점에 로드하는 Lazy-Load 기법을 응용할 수도 있다.

CommonJS

서버 사이드 환경을 중심으로 설계된 규약.

Ajax의 부상과, Google의 V8엔진으로 인해 JavaScript를 브라우저에서뿐만 아니라, 서버사이드 애플리케이션이나 데스크톱 애플리케이션에서도 사용하려고 조직한 자발적 워킹 그룹.

Javascript가 성공하려면, 기술적인 맥락에 치중하는 것보다는 공동으로 표준을 정하는 것이 중요하다는 원칙 하에 CommonJS API 0.1을 발표.

  • 스코프 : 모든 모듈은 자신만의 독립적인 실행 영역이 있어야 한다.
  • 정의 : 모듈 정의는 exports 객체를 이용한다.
  • 사용 : 모듈 사용은 require 함수를 이용한다.
1
2
3
4
5
6
// a.js
var a = 3;
b = 4;
exports.sum = function(c, d) {
return a + b + c + d;
}
1
2
3
4
5
// b.js
var a = 5;
b = 6;
var moduleA = require("fileA");
moduleA.sum(a,b); // 3+4+5+6 = 18

위의 코드는 필요한 파일이 모두 있는 서버사이드 Javascript 환경을 전제로한다. 또한 브라우저에서는 require이나 exports가 없기 때문에 CommonJS 명세를 구현하는 모듈로더를 통해 실행시켜야 한다.

브라우저 환경에서는, 필요한 모듈을 모두 내려받기 전까지는 아무것도 할 수 없으며, 파일 단위의 스코프가 없기 때문에 변수들이 덮어 씌워지게 된다. 그래서 CommonJS는 서버 모듈을 비동기적으로 클라이언트에 전송할 수 있는 모듈 전송 포맷을 추가로 정의하였다.

1
2
3
4
5
6
7
8
9
10
// 모듈 이름을 정의한다.
require.define({"complex-numbers/plus-two": function(require, exports){
//콜백 함수 안에 모듈을 정의한다.
var sum = require("./complex-number").sum;

exports.plusTwo = function(a){
return sum(a, 2);
};
}, ["complex-numbers/math"]);
//먼저 로드되어야 할 모듈을 기술한다.

UMD 만능 모듈 정의

브라우저, Node.js에서 둘다 사용된다. 특별한 모듈로더가 없어도 실행 가능.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD를 위한 define이 존재한다면
define(['b'], factory);
} else if (typeof module === 'object' && module.exports) {
// Node.js나 CommonJS를 위한 module.exports가 존재한다면
module.exports = factory(require('b'));
} else {
// 브라우저 window 객체
root.returnExports = factory(root.b);
}
}(this, function (b) {
// 함수 B를 사용한다.
// exported할 값을 리턴할 수 있다.
return {};
}));

ES6 모듈 포맷

1
2
3
4
5
6
7
8
// lib.js
export function sayHello(){
console.log('Hello');
}

export default function sayBye() {
console.log('Bye');
}
1
2
3
4
5
6
7
import { sayHello } from './lib';
import sayBye from './lib'; // export default를 사용하면 괄호 사용안해도 된다.
// import { sayHello as say } from './lib'; 별칭도 지정 가능

sayHello();
sayBye();
// say(); 별칭으로 실행 가능

전체 모듈을 한 번에 로드 가능

1
2
import * as lib from './lib';
lib.sayHello();

모든 브라우저에서 지원되지 않기 때문에, 코드를 실행하기 전에 Babel과 같은 변환기를 사용해 ES5 모듈 포맷(AMD or CommonJS)로 코드 변환 후 사용하는 것이 권장된다.

모듈 로더

모듈 포맷으로 작성된 모듈을 해석하고 로드한다.

CommonJS와 AMD 모듈을 브라우저에서 실행시키기 위해서는 모듈로더가 필요하다.

참고 : https://web.archive.org/web/20120826205255/http://blog.brianbeck.com/post/10667967423/node-js-require-in-the-browser

모듈 로더는 런타임에 실행된다.

  1. 브라우저에서 모듈 로더를 로드한다.
  2. 모듈 로더에게 어떤 메인 어플리케이션 파일을 로드할 것인지 알려준다.
  3. 모듈 로더는 메인 애플리케이션 파일을 다운로드 하고 해석한다.
  4. 필요한 경우 모듈 로더가 파일을 다운로드 한다.
  • 모듈로더 requireJS 적용
1
2
3
<!-- index.html -->
<script type="text/javascript" src="/js/require.js"></script>
<script type="text/javascript" src="/js/main.js"></script>
1
2
3
4
5
6
7
8
9
// example.js
// AMD나 CommonJS 방식으로 모듈화 시키면 된다.
define(function(){
// 구현 코드 위치
...

return obj;
// 최종 구현 코드의 객체 반환;
}
1
2
3
4
// main.js
require(['example.js'], function(obj) {
// 실제 구현은 여기에...
}

모듈 번들러

모듈 번들러는 빌드 타임에 실행된다. 모듈로더를 대체 하며, 빌드 타임에 번들 파일을 생성하기 위해 모듈 번들러를 실행한다. 브라우저에서는 이 번들 파일을 로드한다.

  • 대표적인 번들러로 weback, browserify가 있다.

webpack : https://skout90.github.io/2017/08/12/Typescript/2.1.%20typescript-webpack-%ED%99%98%EA%B2%BD%EA%B5%AC%EC%B6%95/

Reference

http://d2.naver.com/helloworld/12864

https://github.com/codepink/codepink.github.com/wiki/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%AA%A8%EB%93%88,-%EB%AA%A8%EB%93%88-%ED%8F%AC%EB%A7%B7,-%EB%AA%A8%EB%93%88-%EB%A1%9C%EB%8D%94%EC%99%80-%EB%AA%A8%EB%93%88-%EB%B2%88%EB%93%A4%EB%9F%AC%EC%97%90-%EB%8C%80%ED%95%9C-10%EB%B6%84-%EC%9E%85%EB%AC%B8%EC%84%9C

https://web.archive.org/web/20120826205255/http://blog.brianbeck.com/post/10667967423/node-js-require-in-the-browser

Comment and share

Author's picture

Junho Nam

enjoy while living


full stack web developer


Seoul, Korea