모듈(Module)의 이해

모듈은 특정 기능을 하는 하나의 코드 묶음 단위입니다.

모듈이 모여 하나의 큰 프로그램이 되며, 모듈은 또 다른 모듈의 일부로써 기능을 할 수 있습니다.

모듈의 핵심은 캡슐화 입니다.

모듈 안의 모든 기능들은 모듈 안에서 동작하며, 모듈 밖에서는 접근이 허용된 속성이나 메서드만 사용할 수 있도록 허용됩니다.

ES5 까지는 모듈에 대한 지원이 되지 않았기 때문에, 모듈 비슷한 구조를 즉시 실행 함수를 이용해 구현 했습니다.

예를 들면

var module = (function(){    var _str = '모듈변수';    return {        myfunc: function(){            return _str;        }    }})();console.log(module.api());

이런 방식으로 즉시 실행 함수를 이용해 객체를 반환받아서 모듈처럼 구현을 했습니다.

반환받은 객체는 myfunc 메서드를 호출할 수 있으며, 클로저이기 때문에 _str 변수에 접근할 수 있습니다.

따라서 module.myfunc(); 를 실행하면 '모듈변수' 문자열을 반환받게 됩니다.

모듈 외부에서는 _str 변수에 접근할 수 없습니다.

ES6 부터는 본격적으로 모듈을 지원합니다.

구현 방법이 심플하고 명확하기 때문에 ES6의 새로운 모듈 작성 방법을 사용을 추천합니다.

ES6 에서 새롭게 지원하는 모듈 시스템은 다음의 규칙에 따라 작성합니다.

1. 모듈은 파일단위로 모듈이 구성됩니다.

2. 모듈의 변수, 함수, 클래스 등은 export 키워드로 노출하고 import로 다른 모듈, 페이지에서 가져다 씁니다.

3. 다른 모듈을 가져와(import) 재사용해서 새로운 모듈로 만들 수 있습니다.

4. 모듈은 순환 참조를 할 수 없습니다. 모듈 시스템은 항상 트리 구조로 맨 위에 처음 실행되는 루트 페이지/모듈이 있으며, 한 방향으로만 참조할 수 있습니다.

5. 모듈 이름은 중복되서는 안됩니다. 모듈은 가져오는(import) 시점에 이름을 재 정의해 충돌을 피할 수 있습니다.

모듈의 가장 간단한 예를 들면

app.html

<script type="module" src="./main.js"></script>

moduls.js

export function module(msg){    console.log('msg:'+msg);}

main.js

import {module} from './module.js';module('module run!');

각각의 분리된 파일에 작성한 자바스크립트는 main.js 에서 module.js 파일을 임포트해서 모듈을 로드합니다.

이렇게 모듈을 가져와 쓰는 관계를 의존 관계라고 하고, 의존 관계가 형성되었다고 합니다.

이때 루트는 main.js 가 되고, main.js는 다른 또 다른 여러개의 모듈을 가져올 수 있습니다.

module.js 또한 모듈의 기능 구현에 필요한 다른 모듈을 포함(import)할 수 있습니다.

app.html 에서는 반드시 위 코드처럼 타입(type)을 "module" 로 해서 루트 자바스크립트 파일을 가져와야 합니다.

그렇지 않으면 웹브라우저가 자바스크립트 파일이 모듈이 아니라고 판단하기 때문에 에러가 발생합니다.

모듈을 임포트해서 가저오는 것은 모듈의 참조 주소를 가져오는 것이지 모듈의 값을 가져오는 것이 아닙니다.

자바스크립트의 모든 객체와 마찬가지로 모듈로 객체로서 임포트 시점엥 그 모듈 객체의 참조 주소를 변수에 할당하는 것입니다.

따라서, 모듈 안에서 일어나는 값의 변경 또한 모듈 안에 그대로 반영되어 있게 되고, 모듈을 가져온 코드에서는 변경된 값을 참조만 하는 것입니다.

export 키워드로 내보낼 수 있는 모듈은 var, let, const, function, class 입니다.

export 키워드는 최상위로 정의한 var, let, const, function, class 만 내보낼 수 있습니다.

예를 들어 함수 안에서 export 키워드로 변수를 내보내거나 하는 것은 불가능합니다.

!주의할 점

모듈 시스템은 최근 버전의 웹브라우저 대부분에서 지원하지만, 인터넷 익스플로러는 지원하지 않습니다.

인터넷 익스플로러 실행 호환성을 필요로 하는 코드, 또는 앱을 제작할 경우 모듈이 아닌 다른 구현 방식을 사용해야 합니다.

모듈의 반환 기본값 설정

모듈에 정의한 여러개의 변수, 함수, 클래스, 객체 중 기본으로 내보낼 값을 default 키워드를 사용해 지정할 수 있습니다.

default 키워드는 변수에 사용할 경우 변수 지시자(const, let, var)는 사용할 수 없습니다.

default 키워드는 모듈당 1개만 사용할 수 있습니다.

mymodule.js

export const moduleA = 'moduleA';
export default function moduleB(){console.log('moduleB');}

main.js

import defaultmodule from './mymodule.js';defaultmodule();

default 키워드로 익스포트한 함수, 클래스, 변수 등은 1개만 임포트 하기 때문에 원하는 이름을 사용할 수 있지만, 중괄호로 임포트하는 이름을 감싸면 안됩니다.

default 키워드 없이 여러 개의 함수, 클래스, 변수를 익스포트 한 모듈을 단일 이름으로 임포트하면 에러가 발생하므로 주의해야 합니다.

문자열이나 숫자등 단일 값을 익스포트 할 경우 상수/변수 선언자와 변수명을 사용할 수 없습니다.

export default 'moduleA';

로 익스포트를 해야 합니다.

모듈이름의 재정의

모듈 이름은 의존 관계인 모든 모듈에서 유일해야 합니다.

공개된 수많은 모듈 이름들 사이에 중복이 없을 수 없기 때문에 모듈을 가져오는 시점에 모듈 이름을 재정의할 수 있는 방법을 제공합니다.

import {myMoudle as newMoudle} from './myMoudle.js';

as 키워드를 사용해 오른쪽에 새로운 이름을 지정함으로써 새 이름으로 모듈을 접근할 수 있습니다.

여러 모듈을 하나의 파일에 작성하기

짧은 모듈이나 관련된 모듈들을 모아서 하나의 라이브러리처럼 모듈을 모아서 사용할 수 있습니다.

모듈은 하나의 자바스크립트 파일에 한개가 있는 것이 원칙이고 파일명으로 관리하기 편리하지만, 간단한 메서드 형태인 모듈을 각각의 모듈 파일에 나누어 저장하는 것은 파일 갯수만 많아질 뿐 유지보수하기가 오히려 더 불편합니다.

이런 경우를 위해 한개의 파일에 여러개의 모듈을 작성한 후, 각 모듈을 한꺼번에 불러올 수 있는 수단을 제공합니다.

modules.js

export const moduleA = 'moduleA';
export  function moduleB(){    console.log('moduleB');}
export  class moduleC {    constructor(){        console.log('moduleC');    }}

이렇게 여러개의 모듈을 작성한 경우

import {moduleA, moduleB, moduleC} from './mymodule.js';
console.log(moduleA);moduleB();const modulec = new moduleC();

이렇게 3개의 모듈을 각가 불러와 사용할 수 있습니다.

사용하지 않는 모듈을 제외하고 싶은 경우, 모든 모듈을 로딩할 필요 없이 필요한 모듈만 임포트해서 사용할 수도 있습니다.

import {moduleA} from './mymodule.js';

모듈 갯수가 많은 경우 하나의 모듈 이름으로 불러와 메서드, 또는 속성처럼 사용할 수 있습니다.

"*" 로 모든 모듈을 불려올 경우 반드시 새 모듈이름을 지정해야 합니다.

import * as myModule from './mymodule.js';

이렇게 임포트한 모듈은

console.log(myModule.moduleA);myModule.moduleB();const modulec = new myModule.moduleC();

와 같이 사용할 수 있습니다.

모듈을 다른 자바스크립트 파일에서 쓸 수 있게 내보내는 경우 각각의 변수, 함수, 클래스에 export 키워드를 사용하는 방법 말고, 자바스크립트 끝에 일괄로 내보내는 방법도 있습니다.

예를 들어 위의 modules.js 파일을 재작성하면 

const moduleA = 'moduleA';
function moduleB(){    console.log('moduleB');}
class moduleC {    constructor(){        console.log('moduleC');    }}
export {moduleA, moduleB, moduleC};

와 같이 묶어서 익스포트를 할 수 있습니다.

export 키워드로 내보내지 않은 모듈은 모듈을 임포트한 페이지에서 접근할 수 없습니다.