기록

모듈시스템

해은 2020. 8. 24. 21:42

모듈이란?

여러 기능들에 관한 코드가 모여있는 하나의 파일

 

  • 유지보수성 : 기능들이 모듈화가 잘 되어있다면, 의존성을 그만큼 줄일 수 있기 때문에 어떤 기능을 개선한다거나 수정할 때 훨씬 편하게 할 수 있다.

  • 네임스페이스화 : 자바스크립트에서 전역변수는 전역공간을 가지기 때문에 코드의 양이 많아질수록 겹치는 네임스페이스가 많아질 수 있다. 그러나 모듈로 분리하면 모듈만의 네임스페이스를 갖기 때문에 그 문제가 해결된다.

  • 재사용성 : 똑같은 코드를 반복하지 않고 모듈로 분리시켜서 필요할 때마다 사용할 수 있다.

CommonJS

자바스크립트의 공식 스펙이 브라우저만 지원했기 때문에 이를 서버사이드 및 데스크탑 어플리케이션에서 지원하기 위한 노력이 있었다. 그걸 위해 만든 그룹이 CommonJS이다.

'Common'은 JavaScript를 브라우저에서만 사용하는 언어가 아닌 일반적인 범용 언어로 사용할 수 있도록 하겠다는 의지를 나타내고 있는 것이다.

이 그룹은 JavaScript를 범용적으로 사용하기 위해 필요한 '명세(Specification)'를 만드는 일을 한다. 이 그룹에서 현재까지 정의한 명세로는 Module 명세 1.0, 1.1, 1.1.1 등이 있다. Node.js 모듈도 Module 명세 1.0을 따르고 있다.

다른 모듈을 사용할 때는 require

모듈을 해당 스코프 밖으로 보낼 때에는 module.exports 를 사용하는 방식

 

a.js

const printHelloWorld = () => {
	console.log('Hello Wolrd');
};

module.exports = {
	printHelloWorld
};

b.js

const func = require('./a.js'); // 같은 디렉토리에 있다고 가정
func.printHelloWorld();

 

AMD(Asynchronous Module Definition)

CommonJS 그룹에서 의견이 맞지 않아 나온 사람들이 만든 그룹으로 비동기 모듈에 대한 표준안을 다루는 그룹이다.

CommonJS가 서버쪽에서 장점이 많은 반면에 AMD는 브라우저 쪽에서 더 큰 효과를 발휘한다.

브라우저에서는 모든 모듈이 다 로딩될 때까지 기다릴 수 없기 때문에 비동기 모듈 로딩방식으로 구현을 해놓았다.

이 방식에서 사용하는 함수는 define()  require() 이며 AMD 스펙을 가장 잘 구현한 모듈로더는 RequireJS 이다. 

 

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
      <title>Document</title>
  </head>
  <body>
      <script data-main="index.js" src="require.js"></script>
  </body>
</html>

require.js 파일을 받아서 <script> 태그에 넣어주고 data-main 속성으로 require.js 가 로드된 후에 실행할 자바스크립트 파일 경로를 넣어준다. 즉, require.js 가 로드되자마자 index.js 가 실행되는 구조이다.

 

index.js

require.config({
	baseUrl: '/', paths: { a: 'a', b: 'b', }
});
require(['a'], (a) => {
	a.printA(); 
});

require.config 는 설정부분으로 기본 경로와 각 모듈에 해당하는 경로를 설정해준다. 그 다음 require 를 통해서 첫번째 인자에 해당하는 모듈이 로드되었을 경우에 그걸 a 로 받아서 printA() 함수를 호출하는 콜백함수를 실행한다. 의존성 모듈을 지정해주는 것이다.

 

a.js

define(() => {
  return {
    printA: () => console.log('a')
  }
});

모듈 a는 위와 같이 만들어져 있고 define() 을 통해서 정의된다.

 

UMD(Universal Module Definition)

위에서 살펴본 바로, 모듈 구현방식이 CommonJS 와 AMD로 나뉘기 때문에 그걸 통합하기 위한 하나의 패턴이라고 할 수 있다. 공식 umd 소스코드 를 살펴보면 아래와 같다.

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define([], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.
        module.exports = factory();
    } else {
        // Browser globals (root is window)
        root.returnExports = factory();
  }
}(typeof self !== 'undefined' ? self : this, function () {

    // Just return a value to define the module export.
    // This example returns an object, but the module
    // can return a function as the exported value.
    return {};
}));

AMD, CommonJS, Browser 방식의 모듈을 지원하기 위한 것으로 확인하는 방식은 코드를 살펴보면 다음과 같다.

  • AMD: define() 이 함수이고 define.amd 속성의 객체를 가지고 있다.

  • CommonJS: module 이 객체이고 module.exports 속성의 객체를 가지고 있다.

  • Browser: 따로 특이사항이 없다.

통합하는 방식은 2개의 인자를 전달받는 함수를 실행하는 것으로, 첫번째 인자는 Browser 쪽을 구현할 root 에 넘길 값으로 undefined 이면 this 로 아니라면 self , 즉 window 로 설정한다. 그리고 2번째 인자로 빈 객체 리터럴을 리턴하는 함수를 보낸다. 이렇게 되면 각각의 환경에서 모두 모듈개념을 사용할 수 있게 된다.

 

ES6(ES2015) 방식

import  export 구문을 사용하는 방식으로 나에게는 이 방식이 가장 익숙하다. 하지만 모든 브라우저가 지원하는 것이 아니기 때문에 Babel의 @babel/plugin-transform-modules-commonjs 를 통해 변환시켜서 사용한다. 모듈 A,B가 있고 각각을 export 로 내보내는 방식과 그에 따라 어떻게 import 로 불러오는지 살펴보자.

 

moduleA.js

const A = () => {};
export default A;

moduleB.js

export const B = () => {};

index.js

import A from 'moduleA';
import { B } from 'moduleB';

여기서 눈여겨봐야될 것은 default 의 유무인데 export 를 사용할 때는 named export  default export 를 사용할 수 있다.

단, default export는 모듈 내에서 한번만 사용할 수 있고

named export는 여러번 사용할 수 있다는 것이다.

그렇게 default export로 내보내면 import 에선 내보낸 이름 그대로 바로 사용할 수 있지만,

named export로 내보내면 {} 로 묶어서 불러와야 한다. 이것이 기본적인 사용법이고 별칭(alias)을 as 로 주어서 다른 이름으로 사용할 수도 있고 * 와일드카드를 사용하여 한번에 불러오거나 내보낼 수도 있다.

 

 

'기록' 카테고리의 다른 글

mark tag  (0) 2020.09.15
이벤트 버블링과 캡처링  (0) 2020.09.09
ol counter  (0) 2020.08.12
Javascript 호출스택  (0) 2020.08.09
시맨틱하게 HTML 짜기  (0) 2020.08.03