ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Javascript] Javascript 모듈의 종류
    프로젝트/Share Your Trip 2024. 1. 2. 17:30

    Javasciprt 모듈이 왜 궁금했을까❓

    - Vite의 번들링 원리를 깊이 이해하기 위해 관련이 있는 Javasciprt 모듈 종류에 대해 학습을 진행

     

    Module 이란❓

    • 구현한 코드의 세부 사항을 캡슐화
    • 1개 이상의 값(객체, 함수, 변수 등)을 내보내어 다른 코드에서 쉽게 로드하고 사용할 수 있도록 재사용 가능한 코드 조각

    Module의 조건 📌

    • 코드 추상화
      • 특정 라이브러리에 내재된 기능의 복잡한 구현 방식을 이해하지 않아도 된다.
    • 코드 캡슐화
      • 코드의 잘못된 변경을 막기 위해, 내부에 코드를 숨길 수 있다.
    • 코드 재사용
      • 동일한 코드를 재사용할 필요가 없다.
    • 의존성 관리
      • 코드를 다시 작성하지 않고 의존성 변경이 유연하게 가능하다.

     

    Module 포맷

    • ES5 이하의 버전에서는 모듈을 정의하는 공식적인 문법이 존재하지 않았다.
    • 모듈을 정의할 수 있도록 도와주는 포맷들이 출시되었고, ES6부터는 모듈 포맷을 제공하기 시작했다.

     

    Module 포맷의 종류

    • CommonJS
    • 비동기 모듈 정의(AMD, Asynchronous Module Definition)
    • 만능 모듈 정의(UMD, Universal Module Definition)
    • System.register
    • ES6 모듈 포맷

     

    CommonJS

    • Browser, SSR, Desktop App 등 Javascript를 범용적인 언어로 사용할 수 있도록 조직한 그룹
    • 범용적으로 사용되기 위해 명세(Specification)을 만드는 일을 함
    • CommonJS 포맷은 require와 module.exports를 사용해서 의존성과 모듈을 정의
    var module1 = require('./test1');
    var module2 = require('./test1');
    
    module.exports = function() {
    	// ...
    }
    • CommonJS는 Server Side가 목적이였으며 다음과 같은 문제들을 해결할 수 있다.
      • 서로 호환되는 표준 라이브러리가 없다.
      • 데이터베이스에 연결할 수 있는 표준 인터페이스가 없다.
      • 다른 모듈을 삽입하는 표준적인 방법이 없다.
      • 코드를 패키징해서 배포하고 설치하는 방법이 필요하다.
      • 의존성 문제까지 해결하는 공통 패키지 모듈 저장소가 필요하다.
    • 모듈화(Scope, Definition, Usage)
      • 스코프(Scope) - 모든 모듈은 자신만의 독립적인 실행 영역을 가진다.
      • 정의(Definition) - 모듈 정의는 exports 객체를 이용한다.
      • 사용(Usage) - 모듈 사용은 require 함수를 이용한다.

     

    비동기 모듈 정의(AMD)

    • 필요한 모듈을 네트워크를 통해 내려받을 수 있도록 하는 것(AMD)에 대한 표준안을 다루는 그룹
    • AMD는 CommonJS와 달리 브라우저 내에서의 실행을 중점을 두었고, CommonJS에서 있다가 독립하게 되었다.
    • 브라우저에서 사용되며, define 함수를 사용해서 모듈을 정의한다.
    define(['module1', 'module2'], function (module1, module2) {
    	
    	return function () {};
    });
    • AMD와 CommonJS에서 정의하는 모듈 명세의 차이는 모듈 로드에 있다고 한다.
    • 로컬에 필요한 파일이 있어 바로 사용 가능한 상황인 Server Side에서는 CommonJS 명세가 간결하고 네트워크를 통해 내려받아야 하는 상황에서는 AMD가 유연하다고 한다.

    만능 모듈 정의(UMD)

    • 모듈 구현 방식을 통합하기 위해 나온 패턴
    • 공식 소스코드
    (function (root, factory) {
        if (typeof define === 'function' && define.amd) {
            // AMD. Register as an anonymous module.
            define(['b'], 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(require('b'));
        } else {
            // Browser globals (root is window)
            root.returnExports = factory(root.b);
        }
    }(typeof self !== 'undefined' ? self : this, function (b) {
        // Use b in some fashion.
    
        // 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 - define()이 함수이고 define.amd 속성의 객체를 가지고 있다.
      • CommonJS - module이 객체이고 module.exports 속성의 객체를 가지고 있다.

     

    ES6 (ES2015)

    • import, export를 사용하는 방식으로 모든 브라우저가 지원하는 것이 아니라는 단점이 존재
    • 단점을 해결하기 위해 Bable의 @babel/plugin-transform-modules-commonjs 를 통해 변환하여 사용
    // module1.js
    const A = () => {};
    export default A;
    // module2.js
    export const B = () => {};
    // index.js
    
    import A from 'module1';
    import { B } from 'module1';
    • export를 사용할 때는 named export와 default export를 사용
    • default는 한 번만 사용 가능하고 named export는 여러번 가능
    • default를 통해 내보내면 import에서는 이름 그대로 사용 가능하고 name export의 경우는 {}를 통해 불러와야 한다.
    • as(별칭)를 통해 이름을 변경하여 사용할 수 있으며, *(와일드카드)를 통해 모두 불러올 수도 있다.

     

    Module Loader

    • 모듈 포맷에 맞게 해석하고 로드하는 역할을 하며 런타임에 실행된다.
    • RequireJS, SystemJS 등 모듈 로더가 있다.
    • 모듈 로더 동작 순서
      1. 브라우저에서 모듈 로더를 로드한다.
      2. 모듈 로더에게 어떤 메인 애플리케이션 파일을 로드할 것인지 알려준다.
      3. 모듈 로더는 메인 애플리케이션 파일을 다운로드하고 해석한다.
      4. 필요한 경우 모듈 로더가 파일을 다운로드한다.
      5. 브라우저 개발자 콘솔에서 네트워크 탭을 열면, 모듈 로더에 의해 많은 파일들이 로드된 것을 볼 수 있다.

     

    Module Bundler

    • 모듈 번들러는 모듈 로더를 대체한다.
    • 모듈 번들러는 런타임이 아닌 빌드 타임에 실행된다.
    • Browserify, Webpack 등 모듈 번들러가 있다.
    • 모듈 번들러 동작 순서
      1. 빌드 타임에 번들 파일을 생성하기 위해 모듈 번들러를 실행한다.
      2. 브라우저에서 번들 파일을 로드한다.
      3. 브라우저 개발자 콘솔에서 네트워크 탭을 열면, 모듈 로더에 의해 1개 파일만 로드된 것을 볼 수 있다.
      4. 브라우저에서 모듈 로더를 필요로 하지 않는다. 모든 코드는 번들 안에 포함되어 있다.

     

    기타

    • CommonJS는 서버측에서, AMD는 클라이언트 측에서, ES6 Modules는 Mobile 및 Desktop App 개발에서 사용된다.
    • CommonJS는 모듈을 동기적으로 로드하므로 모듈이 로드될 때까지 다른 코드의 실행이 차단된다. 즉, 서버 측에서 사용할 때는 문제가 없지만 클라이언트에서 사용한다면 성능이 저하될 수 있다.
    • CommonJS에서는 모듈이 로드될 때마다 새로운 객체가 생성되므로, 메모리 사용량이 높아질 수 있다.
    • AMD는 모듈을 비동기적으로 로드하여 다른 코드의 실행을 차단하지 않는다.
    • AMD는 모듈을 로드하기 위해 requireJS를 사용하므로 모듈을 로드할 때, 시간이 걸릴 수 있다.
    • ES6 Modules는 정적으로 모듈을 로드하므로 모듈이 로드될 때까지는 다른 코드의 실행이 차단된다. 마찬가지로 클라이언트에서 성능 저하가 일어날 수 있다.
    • 모듈 로드 시점을 런타임, 컴파일을 지정할 수 있다.

    참고 자료 링크 🧷

    https://colinch4.github.io/2021-01-14/module/

    https://toylee.net/자바스크립트-모듈-시스템-비교-commonjs-vs-amd-vs-es6-modules/

    https://velog.io/@rlaclgns321/모듈-시스템CommonJS-AMD-UMD-ES6

Designed by Tistory.