JavaScript 모듈 시스템의 발전 과정을 이어서 살펴보면, AMD와 CommonJS는 각각 브라우저와 서버 측 환경에 최적화된 솔루션을 제공했지만, 두 환경을 모두 지원하는 모듈을 작성하는 데에는 어려움이 있었습니다.
UMD는 이러한 문제를 해결하고자 등장했습니다.
UMD는 AMD와 CommonJS 모듈 시스템을 결합하여, 동일한 모듈이 브라우저 환경과 Node.js 환경에서 모두 동작할 수 있도록 하기 위해 만들어졌습니다.
이를 위해 UMD는 실행 환경을 자동으로 감지하고, 해당 환경에 맞는 모듈 정의 방식을 사용합니다. 이를 통해 모듈의 재사용성과 호환성을 높였습니다.
⇒ 주요 환경을 감지하고 각 환경에 맞는 방식으로 모듈을 정의해서 사용합니다.
1(function (root, factory) {
2 if (typeof define === 'function' && define.amd) {
3 // AMD 환경
4 define([], factory);
5 } else if (typeof module === 'object' && module.exports) {
6 // CommonJS 환경
7 module.exports = factory();
8 } else {
9 // 전역 변수 (브라우저) 환경
10 root.MyModule = factory();
11 }
12}(typeof self !== 'undefined' ? self : this, function () {
13 // 모듈 정의
14 return {
15 sayHello: function() {
16 return 'Hello, world!';
17 }
18 };
19}));장점
단점
*왜 환경을 감지할 때 AMD → CJS → 전역 변수 순서를 따르는게 일반적인가요?
아쉬운 점은 UMD는 AMD와 CommonJS를 지원하기 위해 복잡한 조건문과 환경 감지가 필요하다는 것입니다.
이때 모듈 시스템의 표준화를 통해 모듈 관리를 개선하고 최적화 하기 위해서 ESM이 등장하게됩니다.
ECMAScript 표준에 따라 JavaScript 모듈을 정의하고 사용하는 방식입니다.
JavaScript 모듈 시스템의 표준으로, 브라우저와 서버 환경 모두에서 사용됩니다.
최신 브라우저와 Node.js에서 네이티브로 지원되기에 추가적인 라이브러리나 도구 없이 모듈을 사용할 수 있습니다.
장점
import를 사용한 동적 임포트 예제
1function loadModule() {
2 import('./module.js').then(module => {
3 module.default();
4 });
5}
6
7document.getElementById('loadButton').addEventListener('click', loadModule);*정적 분석이란? 코드 실행 없이 소스 코드를 분석하는 것을 의미합니다. 코드의 구조, 의존성, 사용되지 않는 코드 등을 파악할 수 있습니다.
단점
1// moduleA.js
2export const foo = 'foo';
3
4// moduleB.js
5import { foo } from './moduleA.js';
6console.log(foo);위의 코드에서, moduleB.js는 moduleA.js에 의존합니다. 정적 분석을 통해 이 관계가 그래프로 표현됩니다.
//그래프를 시각화한 예시
├── moduleA.js
│ └── moduleC.js
└── moduleB.js
└── moduleC.js1// moduleA.js
2export const foo = 'foo';
3export const bar = 'bar'; // 사용되지 않음
4
5// moduleB.js
6import { foo } from './moduleA.js';
7console.log(foo);위의 코드에서 bar는 moduleB.js에서 사용되지 않으므로, 트리 셰이킹을 통해 번들에서 제거될 수 있습니다.
1// main.js
2import { foo } from './moduleA.js';
3
4function loadModule() {
5 import('./moduleB.js').then(moduleB => {
6 moduleB.doSomething();
7 });
8}
9
10console.log(foo);위의 코드에서 moduleB.js는 동적 import()를 통해 비동기적으로 로드됩니다. 이를 통해 초기 로딩 성능을 최적화할 수 있습니다.
1// main.js
2import { foo } from './moduleA.js';
3import { bar } from './moduleC.js';
4
5console.log(foo);
6console.log(bar);위의 코드에서 moduleA.js와 moduleC.js의 의존성을 분석하여, 필요한 모듈만 포함하는 최적화된 번들을 생성합니다.
ESM이 도입된 이후 UMD와 CommonJS 모듈 시스템을 사용하는 상황은?
UMD는 라이브러리 배포와 다양한 환경 지원을 위해, CommonJS는 Node.js환경과 레거시 코드베이스 유지보수를 위해 사용됩니다.
UMD를 사용하다가 ESM으로 마이그레이션 하려면 어떤 것들이 고려될까?
이렇게 모듈 시스템의 변천사를 살펴봤습니다.
각 모듈 시스템들은 이전 방식의 아쉬운 점들을 보완하기 위해 계속 발전해왔습니다.
모듈 시스템의 발전은 UX/DX의 엄청난 상승을 가져왔다고 느꼈는데요.
(CJS가 없었더라면 지금의 Framework들도 없었을 것이고 Node.js가 없었으면 npm이 없었으면.. )
위 경우들처럼 개발생태계에 큰 기여를 하는 생에 꼭 경험해보고싶습니다.