본문 바로가기
Web/JavaScript

[JavaScript] 자바스크립트 모듈 시스템을 마스터 해보자

by 서상혁 2022. 8. 6.

들어가며

시작은 미약했으나, 그 끝은 창대하리라

 

'시작은 미약했으나, 그 끝은 창대하리라' 이 말은 딱 자바스크립트를 위한 말이 아닐까 싶습니다. 초창기 자바스크립트가 생겨났을 때, 이렇게 대표적인 언어로 발전할 것이라고 예상을 했을까요? 자바스크립트는 탄생 이래로 정말 많은 변화와 발전과정을 거쳐왔습니다.

 

자바스크립트의 탄생부터 FE 세계에 있던 분이 아니라면, 저처럼 타입스크립트 시대부터 제데로 공부를 시작한 분이라면, JS의 모듈 시스템에 대해 정확히 이해하고 있기가 쉽지 않습니다. ts 파일을 직접 실행시킬 때 import 문제로 실행이 되지 않아 애먹은 적이 있었죠.

 

{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
    }
}

tsconfig 의 compilerOptions > module . 정확히 이해하고 사용하셨나요? (저는 아니었답니다.. 🥲)

 

그런 의미에서 오늘은 자바스크립트의 module 시스템의 역사, 번들링 에 대해 정리해보려고 합니다.

 

헌데, 제가 js 코어의 전문가라 특출난 내용을 쓸 수 있는 것도 아니고 이미 js 모듈에 대해서는 좋은 레퍼런스들이 많기 때문에, 다양한 레퍼런스를 살펴보고 목차별로 좋은 게시글을 공유드리고 기타 일반적으로 모르고 있거나 궁금할 만한 점들에 대한 내용을 기록하려고 해요.

 

 

 

읽어볼 문서 링크들 모음

 

출처 : https://wormwlrm.github.io/2020/08/12/History-of-JavaScript-Modules-and-Bundlers.html

 

JS의 큰 그림을 정말 재미있고 깔끔하게 요약해두신 분이 있네요! 이 사진은 꼭 공유드리고 싶었습니다.

 

 

 

전반적인 JS 모듈 내용에 대한 글 모음

 

Module | PoiemaWeb

모듈이란 애플리케이션을 구성하는 개별적 요소로서 재사용 가능한 코드 조각을 말한다. 모듈은 세부 사항을 캡슐화하고 공개가 필요한 API만을 외부에 노출한다. ES6 모듈은 단 두 개의 키워드 e

poiemaweb.com

 

JavaScript 번들러로 본 조선시대 붕당의 이해 - 재그지그의 개발 블로그

JavaScript 모듈 표준화의 제안부터 현대 모듈 번들러 등장까지의 역사를 정리해봅니다.

wormwlrm.github.io

 

JavaScript modules · V8

This article explains how to use JS modules, how to deploy them responsibly, and how the Chrome team is working to make modules even better in the future. JS modules (also known as “ES modules” or “ECMAScript modules”) are a major new feature, or r

v8.dev

 

JavaScript modules - JavaScript | MDN

이 가이드는 자바스크립트 모듈 구문을 시작하는데 필요한 모든 것을 제공합니다.

developer.mozilla.org

 

[javascript] CJS, AMD, UMD, ESM 이란? / 사용법

👉 CJS, AMD, UMD, ESM의 목적 처음 JS는 모듈로 가져오거나 내보내는 방법이 없어, 하나의 파일에 모든 기능을 담아야 했다. JS 프로그램을 모듈로 개발하고, 배포할 수 있게 하기 위하여, CJS, AMD, UMD,

defineall.tistory.com

 


 

*.mjs란 무엇일까

EcmaScript의 모듈용 js임을 명확히 하기 위해 사용합니다. CommonJS 모듈은 cjs 로 사용한다고 하네요.

<script type="module" src="lib.mjs"></script>
<script type="module" src="app.mjs"></script>

 

사실 우리는 주로 Webpack 번들러를 이용하거나, 번들링이 내장된 프레임워크의 단위로 코드를 짜기에 따로 설정할 일이 별로 없습니다.

 

 

 

nomodule 이란?

모듈 대체 스크립트를 의미합니다.

type 특성이 module을 지원하는 브라우저는 nomodule 특성을 가진 모든 <script>를 무시합니다. 그러므로 모듈 스크립트를 사용하면서도, 미지원 브라우저를 위한 대체 스크립트를 nomodule로 표시해 제공할 수 있습니다.

예시

<script type="module" src="main.mjs"></script>
<script nomodule src="fallback.js"></script>

출처 : https://developer.mozilla.org/ko/docs/Web/HTML/Element/script#%EB%AA%A8%EB%93%88_%EB%8C%80%EC%B2%B4_%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8

 

 

 

tsconfig의 module 설정 실습

tsconfig 의 module 설정으로 실습을 해보았습니다.

구체적으로 문법을 외운다기 보다는, 각 모듈 방식의 핵심이 무엇인지, 어떤 문법을 가져가는지 정도만 이해하면 될 것 같아요.

 

테스트를 위한 기본 ts 파일입니다.

 

module.ts

export const a = 1
export default { 'module' : 1 }

 

core.ts

import { a } from './module'

 

이제 각각의 모듈 방식으로 컴파일링해볼게요.

 

$ tsc

 

"module" : "commonjs"

JS를 브라우저 뿐 아니라 다른 런타임에서도 실행가능하게끔 한다. nodeJS 의 방식.
{
  "compilerOptions": {
    "strict": true,
    "target": "es2016",
    /* Modules */
    "module": "commonjs"
  }
}

 

 

module.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.a = void 0;
exports.a = 1;
exports.default = { 'module': 1 };

core.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const module_1 = require("./module");
console.log({ a: module_1.a });

 

 

일반적인 require 형태로 임포트됩니다!

 

Modules | Node.js v10.24.1 Documentation

Modules# In the Node.js module system, each file is treated as a separate module. For example, consider a file named foo.js: const circle = require('./circle.js'); console.log(`The area of a circle of radius 4 is ${circle.area(4)}`); On the first line, foo

nodejs.org

 

"module" : "es2022"

가장 최근에 통합된 방식, ESM, ES2020, ESNext 도 사실상 동일하다.
{
  "compilerOptions": {
    "strict": true,
    "target": "es2016",
    /* Modules */
    "module": "es2022"
  }
}

 

module.js

export const a = 1;
export default { 'module': 1 };

 

core.js

import { a } from './module';
console.log({ a });

 

es2022 는 타입스크립트 문법과 동일하게 그대로 컴파일링 됩니다!

es2022 는 최상위 await 구문도 사용 가능해요.

 

ECMAScript 2022 살펴보기 | 요즘IT

자바스크립트(JavaScript)는 ECMAScript를 준수하는 언어이기 때문에, 이번 발표는 JavaScript 사양의 변경으로도 해석할 수 있습니다. 사실 표준이 되기 전에도 이미 대부분의 브라우저가 이를 지원하고

yozm.wishket.com

 

"module" : "amd"

비동기를 지원하는 방식
{
  "compilerOptions": {
    "strict": true,
    "target": "es2016",
    /* Modules */
    "module": "amd"
  }
}

 

module.js

define(["require", "exports"], function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.a = void 0;
    exports.a = 1;
    exports.default = { 'module': 1 };
});

core.js

define(["require", "exports", "./module"], function (require, exports, module_1) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    console.log({ a: module_1.a });
});

amd 의 임포트 방식은 일반적으로 사용하는 문법과는 사뭇 다르죠.

 

GitHub - amdjs/amdjs-api: Houses the Asynchronous Module Definition API

Houses the Asynchronous Module Definition API. Contribute to amdjs/amdjs-api development by creating an account on GitHub.

github.com

 

"module" : "umd"

AMD와 CommonJS가 서로 호환되지 않는 문제를 해결하기 위한 방식
{
  "compilerOptions": {
    "strict": true,
    "target": "es2016",
    /* Modules */
    "module": "umd"
  }
}

 

module.js

(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.a = void 0;
    exports.a = 1;
    exports.default = { 'module': 1 };
});

core.js

(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "./module"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    const module_1 = require("./module");
    console.log({ a: module_1.a });
});
 

blog.rhostem.com

프론트엔드 웹 개발 기술 블로그

blog.rhostem.com

 

 

"module" : "system"

SystemJS 에서 사용하는 방식
{
  "compilerOptions": {
    "strict": true,
    "target": "es2016",
    /* Modules */
    "module": "system"
  }
}

module.js

System.register([], function (exports_1, context_1) {
    "use strict";
    var a;
    var __moduleName = context_1 && context_1.id;
    return {
        setters: [],
        execute: function () {
            exports_1("a", a = 1);
            exports_1("default", { 'module': 1 });
        }
    };
});

core.js

System.register(["./module"], function (exports_1, context_1) {
    "use strict";
    var module_1;
    var __moduleName = context_1 && context_1.id;
    return {
        setters: [
            function (module_1_1) {
                module_1 = module_1_1;
            }
        ],
        execute: function () {
            console.log({ a: module_1.a });
        }
    };
});
 

GitHub - systemjs/systemjs: Dynamic ES module loader

Dynamic ES module loader. Contribute to systemjs/systemjs development by creating an account on GitHub.

github.com

 

 

 

 

728x90

댓글