JS 모듈에 대하여

차유림·2021년 9월 5일
0

모듈

모듈은 1개 이상의 을 내보내는 JS파일이다.
모듈을 통해 기능과 관심사의 분리를 할 수 있다.

자바스크립트 모듈 선언 포맷인 AMDCommonJS,
브라우저와 노드에서 모두 사용가능하도록 만든 UMD,
브라우저에서 동작하는 모듈 기능인 ESM 에 대해 알아본다.

AMD (Asynchronous Module Definition)

모듈을 선언하면서 의존하고 있는 모듈도 함께 명시한다.
필요에 따라 비동기적으로 의존모듈을 불러온다.
실행할 코드가 필요한 파일만 가져오므로 성능이 좋다.
의존모듈을 불러온 후, 코드를 실행할 수 있어 오류가 줄어든다.
AMD 포맷으로 구현한 라이브러리에는 Require.js, Dojo Toolkit이 있다.


// define(의존모듈 배열로 선언, function(){})

define(['jQuery'], function($){
    function withjQuery(){}; // 함수선언
    return withjQuery; // 외부에 함수 노출
}

CJS (CommonJS)

브라우저가 아닌 환경에서 js 모듈 포맷을 확립했다.
서로다른 개발환경(서버, desktop, app)에서 만든 js코드를 공유하기 위해
2009년 serverJS 프로젝트로 시작했고, CJS로 이름 변경됨
Node.js 에서 구현되어 있다.


// 의존모듈 선언
var $ = require('jQuery');

// 함수 선언
function withjQuery(){};

// 외부에 함수 노출
module.exports= withjQuery;

UMD (Universal Module Definition)

노드와 브라우저 환경 모두를 지원하도록 만든 것이 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 {};
}));

ESM (ES6 Module)

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'

📚 참고자료

profile
🎨프론트엔드 개발자💻

0개의 댓글