Intro
Java나 Python과 같은 OOP 언어들에서는 Class라는 이름으로 객체지향 프로그래밍을 구현해왔다.
OOP의 특징으로는 널리 알려져있듯 encapsulation(캡슐화), inheritance(상속), polymorphism(다형성)이 있다.
그 중 자바스크립트의 scope를 공부하고 있었는데 encapsulation과 관련있었으며, 자연스럽게 class와 비슷하게 흉내내서 기능하는 자바스크립트의 module 공부하게 되었다.
javascript에서는 scope를 활용해서 캡슐화를 구현할 수 있다. 그래서 scope를 이용하여 외부로부터 보호하려는 노력이 있어왔다. ES5에서는 당연히도 함수의 scope를 이용해 encapsulation을 시도했다. 또한 자신의 스코프의 참조를 가지는 클로저를 이용해 내부 속성들을 사용할 수 있게 했다.
TOC
Module
- 즉시 실행 함수 표현식(IIFE, Immediately Invoked Function Expression)
- 노출식 모듈 패턴(Revealing Module Pattern)
- AMD(Asynchronous Module Definition)
- UMD
- CommonJS
AMD는 크게 중요하지 않다고 생각되어 define
을 사용한다고만 하고 넘어가겠다.
즉시 실행 함수 표현식(IIFE, Immediately Invoked Function Expression)
함수를 즉시 호출시켜 스코프를 오염시키지 않는 방식이다.
function moduleFunction() {
var a = 3;
function helloWorld() { console.log('Hello'); }
return {
a : a,
sayHello: helloWorld
}
}()
moduleFunction.sayHello();
doSomething(moduleFunction.a);
함수를 바로 실행시킨 뒤 object를 반환하여 전역 네임을 한번만 사용하여 스코프를 오염시키지 않는다. 또한 외부에서 내부 코드로의 접근을 차단하여 보호한다. 모듈의 개념을 잘 이행하고 있다. 다만 의존성관리가 되지 않는다. 코드를 재작성하지 않으면 다른 파일에서 재사용이 불가능하다.
노출식 모듈 패턴(Revealing Module Pattern)
IIFE와 크게 다르는 않다. 반환값을 변수에 담으면 되는데 singleton 패턴이다.
var singleton = (function moduleFunction() {
var a = 3;
function helloWorld(){
console.log('Hello');
}
return {
a : a,
sayHello: helloWorld
}
})();
singleton.sayHello();
doSomething(singleton.a);
비슷하다. 문제는 의존성관리가 안된다.
Node JS의 모듈화
의존성 관리 문제 때문에 ES5에서는 AMD, UMD, CommonJS와 같은 모듈 포맷을 사용했는데 여기서는 가장 흔히 알고있는 CommonJS 방식에 대해 알아보자.
var dep1 = require('./dep1');
var dep2 = require('./dep2');
module.exports = function(){
// ...
}
CommonJS는 위와 같은 구조를 가진다. 흔히들 봤었던 require
, module.exports
구조다. 외부의 dep1.js
를 module.exports
로 내보내고 require()
로 불러와 사용하는 방식이다. 사실 내부적으로는 IIFE와 크게 다르지 않다. 내보낼 module을 파라미터로하여 IIFE를 실행시키고 반환하는 방식이다.
하지만 파일 외부에서 작성하고 불러온다는 점에서 많이 나아졌다.
ES6 Module
ES6에서는 모듈을 사용하기 위한 문법을 지원한다. 바로 export
다.
// some.js
export function helloWorld(){
console.log('Hello');
}
function somePrivateFunction(){
// ...
}
// something.js
import { helloWorld as hello } from './some';
// import * from './some' 도 가능하다.
hello();
이렇게 하면 파일을 분리하고 그 파일에서 필요한 부분만 반환하여 사용할 수 있게 된다. 또한 재사용성이 극대화되어 의존성 관리도 가능하게 된다.
현재 예제 코드에서는 함수만 반환했지만 당연히 Object도 가능하다.
또한 단일 값을 반환 할 경우 default
형식을 지원한다. 이 경우 {}
를 사용하지 않아도 된다.
// Export default function
export default function sayHello(){
console.log('Hello');
}
// Export non-default function
export returnObject;
------------------------------------
function sayGoodbye(){
console.log('Goodbye');
}
// Export simple value
const apiUrl = '...';
// Export object
export const settings = {
debug: true
}
returnObject = {
goodbye : sayGoodbye,
apiUrl : apiUrl
}
export returnObject;
import sayHello, { returnObject, settings } from './lib';
sayHello();
returnObject.sayGoodBye();
하지만 현재 이러한 모듈 포맷은 모든 브라우저에서 지원하지 않는다.
Babel과 같은 변환기를 통해 ES5에서 사용하던 방식인 AMD나 CommonJS 형식으로 코드를 변환한 뒤 사용한다.