JavaScript - Module

이소라·2022년 11월 22일
0

JavaScript

목록 보기
12/22

Module

  • 모듈

    • 애플리케이션을 구성하는 요소
    • 재사용 가능한 코드 조각
    • 파일 단위로 분리되어 있음
    • 기능별로 분리되어 작성되므로 코드의 단위를 명확히 분리함
      • 기능을 개선하거나 수정하기 쉽기 때문에 유지보수성이 높음
      • 재사용성이 좋음
  • JavaScript는 파일마다 독립적인 스코프를 갖지 않고 전역 객체를 공유함

    • 전역 변수가 중복되는 문제점이 발생함
    • 모듈화를 할 수 없음



JavaScript의 모듈화

CommonJS

  • CommonJS 방식의 모듈화
    • CommonJS : 브라우저뿐만 아니라 서버나 데스크탑 어플리케이션에서 JavaScript 지원하기 위해 만든 그룹
    • Node.js에서 CommonJS 방식을 사용하고 있음
    • 다른 모듈을 사용할 때 : require
    • 모듈을 해당 스코프 밖으로 내보낼 때 : module.exports
      • module
        • 현재 모듈에 대한 정보를 갖고 있는 객체임
        • exports객체를 갖고 있음
// a.js
const print = () => {
  console.log('hello');
}

module.exports = {
  print
};

// b.js
const func = require('./a.js');
func.print();
  • 함수를 모듈 밖으로 내보내고자 할 때 module.exportsexports 둘 다 사용 가능함
    • module.exports는 빈 객체를 참조함
    • exportsmodule.exports를 참조함
    • require는 항상 module.exports를 반환함
exports.print = print;
module.exports = { print };
  • exports를 사용하는 이유
    • exports는 항상 module.exports를 잠조하므로, exports를 사용하면 module.exports를 수정하지 않고 객체의 멤버를 만들거나 수정할 수 있음
    • exports에 어떤 값을 할당하더라도 requiremodule.exports를 반환하기 때문에 잠재적인 버그를 피할 수 있음

AMD(Asynchronous Module Definition)

  • AMD : 브라우저에서 비동기 모듈 로딩 방식을 구현함
  • 사용하는 함수 : define(), require()
  • 모듈 로더: RequireJS
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Document</title>
</head>
<body>
  <script data-main="index.js" src="require.js"></script>
</body>
</html>
  • 위 html의 실행 과정
    1. <script> 태그의 src 속성에 할당된 require.js 파일을 받음
    2. require.js가 로드된 후, <script> 태그의 data-main 속성에 할당된 index.js가 실행됨
require.config({
  baseUrl: '/',
  paths: {
    a: 'a',
    b: 'b',
  }
});

require(['a'], (a) => {
  a.printA();
});
  • index.js 파일
    • require.config
      • baseUrl: 기본 경로 설정
      • paths: 각 모듈에 해당하는 경로 설정
    • require
      • 첫 번째 인자에 해당하는 모듈이 로드되었을 때, 콜백함수를 실행함
define(() => {
  return {
    printA: () => console.log('a');
  }
})
  • a.js 파일
    • define : 콜백함수가 실행되기 전에 로드되어야 할 모듈을 정의함

UMD(Universal Module Definition)

  • UMD
    • AMD와 CommonJS 모듈 구현 방식을 통합하기기 위해 만든 모듈 패턴

    • AMD, CommonJS, Browser 방식의 모듈을 지원

    • 2개의 인자를 전달받는 함수를 실행함

      • 첫 번째 인자 : Browser쪽을 구현할 root에 넘길 값이 undefinedthis로, 아니면 window로 설정함
      • 두 번째 인자 : 빈 객체 리터럴을 리턴하는 함수를 보냄
// 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 {};
}));

ES6(ES2015) 방식

  • 모듈을 받을 때 : import
  • 모듈을 내보낼 때 : export
  • 모든 브라우저에서 지원하지 않으므로, @babel/plugin-transform-modules-commonjs 를 통해 변환시켜서 사용함
// module.A.js
const A = () => {};
export default A;

//moduleB.js
export const B = () => {};

//index.js
import A from 'moduleA';
import { B } from 'moduleB';
  • export를 사용할 때, named exportdefault export를 사용할 수 있음
    • default export
      • 모듈 내에서 한번만 사용 가능함
        • default export로 내보내면 import에서 내보낸 이름을 그대로 사용할 수 있음
    • named export
      • 모듈 내에서 여러번 사용 가능함
        • named export로 내보내면 {}로 묶어서 불어와야 함
        • as로 별칭을 주어서 다른 이름으로 사용 가능함
        • * 와일드카드를 사용하여 한번에 불러오거나 내보낼 수 있음



ES6 Module

1. Module Scope

  • ES6 모듈은 독자적인 모듈 스코프를 가짐
    • ES6 모듈에서 var 키워드로 선언한 변수는 전역 변수가 아니고, window 객체의 프로퍼티도 아님
    • 모듈 내에서 선언한 변수는 모듈 외부에서 참조할 수 없음
      • 스코프가 다르기 때문임
// foo.mjs
var x = 'foo';
var y = 1;

console.log(x); // foo
console.log(window.x); // undefined


// bar.mjs
var x = 'bar';

console.log(x); // bar
console.log(window.x); // undefined
console.log(y); // ReferenceError: x is not defined

2. export 키워드

  • 모듈 안에서 선언한 식별자를 외부에 공개하여 다른 모듈들이 참조할 수 있게 하기 위해 export 키워드를 사용함
    • 선언된 변수, 함수, 클래스 모두 export 할 수 있음
// 변수
export const pi = Math.PI;

// 함수
export function square(x) {
  return x * x;
}

// 클래스
export class Person {
  constructor(name) {
    this.name = name;
  }
}
  • export 대상들을 모아 하나의 객체로 구성하여 한번에 export 할 수 있음
const pi = Math.PI;

function square(x) {
  return x * x;
}

class Person {
  constructor(name) {
    this.name = name;
  }
}

export { pi, square, Person };

3. import 키워드

  • 모듈에서 export한 대상을 로드하기 위해 import 키워드를 사용함
    • 모듈이 export한 식별자로 import함
    • ES6모듈의 파일 확장자 생략 불가능
import { pi, square, Person } from './lib.mjs';

console.log(pi);         // 3.141592653589793
console.log(square(10)); // 100
console.log(new Person('Lee')); // Person { name: 'Lee' }
  • 모듈이 export한 식별자를 각각 지정하지 않고 하나의 이름으로 한번에 import 가능함
    • 이때 import되는 식별자는 as 뒤에 지정한 이름의 객체에 프로퍼티로 할당됨
import * as lib from './lib.mjs';

console.log(lib.pi);         // 3.141592653589793
console.log(lib.square(10)); // 100
console.log(new lib.Person('Lee')); // Person { name: 'Lee' }
  • 이름을 변경하여 import할 수 있음
import { pi as PI, square as sq, Person as P } from './lib.mjs';

console.log(PI);    // 3.141592653589793
console.log(sq(2)); // 4
console.log(new P('Kim')); // Person { name: 'Kim' }
  • 모듈에서 default export를 한 번 사용할 수 있음
// lib.mjs
export default function (x) {
  return x * x;
}
  • 다만 default export를 할 때, var, let, const 키워드를 사용할 수 없음
// lib.mjs
export default () => {};
// => OK

export default const foo = () => {};
// => SyntaxError: Unexpected token 'const'
  • default export한 모듈은 {} 없이 임의의 이름으로 import함
// app.mjs
import square from './lib.mjs';

console.log(square(3)); // 9



참고

0개의 댓글