모듈

김하은·2023년 5월 9일
0

모듈이란

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

  • 유지보수성: 기능들이 모듈화가 잘 되어있다면, 의존성을 줄일 수 있다. 따라서 어떠한 기능을 개선하거나 수정할 때 훨씬 편하다.

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

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

이러한 장점들을 살리기 위해 모듈이라는 개념이 필요하였고, 자바스크립트에서는 모듈을 개발하기 위해 여러가지 시도들을 했다.
대표적으로 CommonJS, AMD, UMD, ES6둥이 있다.

CommonJS

자바스크립트 공식 스펙이 브라우저만 지원했기에 이것을 서버사이드 및 데스크탑 어플리케이션에서 지원하기 위한 노력이 있었다.
이것을 위해 만든 그룹이 CommonJS이고 여기에서는 자바스크립트가 범용적인 언어로 쓰이기 위한 스펙을 정의한다.
그룹을 만들었을때 범용적인 언어로 만들기 위해서는 모듈화의 개념이 필요했으며 따라서 이 그룹만의 모듈방식을 정의하였는데 그것이 바로 CommonJS방식의 모듈화이다.

다른 모듈을 사용시 require 을 사용하고, 모듈을 해당 스코프 밖으로 내보낼 시에는 module.exports를 사용하는 방식으로 Node.js에서 사용하는 방식이다.

내보내는 쪽 a.js 파일

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

module.exports = {
printHelloWorld
};


가져와 쓰는 쪽  `b.js`

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

여기서 module.exports의 module은 현재 모듈애 대한 정보를 가지는 객체이다.
이것은 얘약어이고 이 안에 많은 속성이 있으며 exports객체를 가지고있다.

exports vs module.exports

module.exports외에도 exports를 사용하기도 하는데 이 관계에 대한 이해가 필요하다.

  • module.exports는 빈 객체를 참조한다.
  • exportsmodule.exports를 참조한다.
  • require 은 항상 module.exports 를 리턴받는다.

함수를 모듈밖으로 내보내고자 할때

exports.printHelloWorld = printHelloWorld;
module.exports = { printHelloWorld };

두가지 다 사용이 가능하다. 그러면 왜 두가지나 설정이 되어있을까??

이유::
exports는 항상 module.exprots 를 참조하기에 exports를 사용하면 module.exports를 직접 수정하지 않고 객체의 맴버를 만들거나 수정하는 방법으로 사용한다. 따라서 exports에 어떤 값이나 객체를 할당했더라도 결국 requiremodule.exports를 리턴받기에 잠재적은 버그를 피할 수 있다.

AMD

CommonJS그룹에서 의견이 맞지 않아 나온 사람들이 만든 그룹.
비동기 모듈에 대한 표준안을 다루는 그룹니다.
CommonJS가 서버쪽에서 장점이 많다면 AMD는 브라우저 쪽에서 더 큰 효과를 발휘한다.
브라우저에서는 모든 모듈이 다 로딩될때까지 기다리지 않기에 비동기 모듈 로딩방식으로 구현해놓았다.
이 방식에서 사용하는 함수는 define()require() 이고
이 스펙을 잘 구현한 모듈러는 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파일을 받아 스크립트 태그에 넣어주고 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()을 통해 정의된다.
여기서도 require에서 의존성모듈을 설정해 준것처럼... 콜백함수가 실행되기 전에 로드될 모듈들을 정의할 수 있다.

UMD

위에서 모듈구현방식이 CommonJS와 AMD로 나뉘었는데 이것을 통합하기 위한 패턴이 UMD이다.

공식 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 {};
}));
  • AMDdefine() 이 함수이고 define.amd 속성의 객체를 가지고 있다.
  • CommonJSmodule 이 객체이고 module.exports 속성의 객체를 가지고 있다.
  • Browser: 따로 특이사항이 없다.

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

ES6방식(ES2015 ~)

import 와 export를 사용하는 방식.
내가 계속사용해온 방식.
하지만 모든 브라우저가 지원하지는 않는다...따라서
Babel의 @babel/plugin-transform-modules-commonjs 를 통해 변환시켜서 사용한다.
간단하게 export로 내보내고, import로 받아온다!!

다만 export로 내보낼때 export default를 사용할 수도 있는데 이것은 모듈내 한번(한 파일에 한번) 만 사용이 가능해 내보낸 이름 그대로 사용할 수 있고, export만 사용하면 중괄호로 묶어서 불러와야한다.
as로 별칭을 사용할 수 있고, *이라는 전채를 뜻하는 와일드 카드를 사용해 한번에 불러오거나 내보낼 수도 있다

0개의 댓글