도은
모듈형이란, 서로 의존성이 낮은 기능들이 모듈로써 저장된 형태를 의미 이번 장에서는 ES5 문법을 사용하는 AMD, CommonJS, UMD 방식을 살펴본다.
10.1 스크립트 로더(script loader)
- 자바스크립트 파일을 웹페이지에 동적으로 로드하거나 제어할 수 있도록 해주는 도구 또는 메커니즘
- 모듈 로더(module loader)라고 부르기도 하는 듯
- 의존성 주입과 모듈화를 함께 처리
🤔 스크립트 로더는 왜 필요한 것?
- 브라우저가 지원하는 모듈 포맷과, 우리가 로드하려는 스크립트의 포맷이 다르기 때문
- 브라우저는
import/export
가 있는ESM
만 이해- 지원하지 않는
AMD
,CJS
을 냅다 사용하면 - 브라우저는 이걸 실행하지 못함 (
require is not defined
)
- 지원하지 않는
- 스크립트 로더는
- 스크립트를 로드하고
- 내부 포멧을 확인한 뒤에 (CJS인지 AMD인지)
- 이를 브라우저에서 쓸 수 있는 방식으로 감싸서 잘 실행시켜주게 된다.
10.2 AMD
- 모듈과 의존성 모두를 비동기적으로 로드할 수 있도록 설계된 모듈 정의 방식
- 비동기적이면서도 높은 유연성 → 개발 과정에서 흔히 발생하는 코드와 모듈 간 긴밀한 결합을 줄여주는 등의 장점
모듈 알아보기
-
define
- 이름이 있는 모듈 혹은 익명 모듈을 정의하는 데 사용
define( module_id, /* 선택 인자 */ [dependencies], /* 선택 인자 */ definition function {} /* 모듈이나 객체를 인스턴스화하는 함수 */ )
- 이름이 있는 모듈 혹은 익명 모듈을 정의하는 데 사용
-
require
-
최상위 자바스크립트 파일이나 모듈 내에서 의존성을 동적으로 가져오고자 할 때 사용
// "foo"와 "bar"를 외부 모듈이라고 가정 // 이 예제에서는 외부에서 로드된 두 모듈이 콜백 함수의 인자로 전달 require(['foo', 'bar'], function (foo, bar) { // 나머지 코드 foo.doSomething(); }); // 🔘 동적으로 로도딘 의존성 define(function (require) { var isReady = false, foobar; // 모듈 내부에서 require를 통해 의존성을 가져온다 require(['foo', 'bar'], function (foo, bar) { isReady = true; foobar = foo() + bar(); }); // 이렇게 ㄹ모듈을 반환 return { isReady: isReady, foobar: foobar, }; });
-
10.3 CommonJS
- 서버 사이드에서 모듈을 선언하는 간단한 API를 지정하는 모듈 제안
- AMD와 달리 I/O, 파일 시스템, 프로미스 등 더욱 광범위한 범위
CommonJS 시작하기
- AMD와 달리 CJS는 모듈을 함수로 감싸는 작업이 필요하지 않다.
exports
변수는 다른 모듈에 내보내고자 하는 객체를 담는다.require
함수는 다른 모듈에서 내보낸 객체를 가져올 때 사용하는 함수다.
// `package/lib` 외부 라이브러리를 가져온다.
var lib = require('package/lib');
// 모듈 내부 로직을 정의
function foo() {
lib.log('hello world');
}
// foo 함수를 다른 모듈에서 사용할 수 있도록 내보낸다.
exports.foo = foo;
👇 위 코드를 AMD로
define(function (require) {
var lib = require('package/lib');
function foo() {
lib.log('hello world');
}
return {
foobar: foo,
};
});
10.4 UMD
define(function (require, exports, module) {
var shuffler = require('lib/shuffle');
exports.randomize = function (input) {
return shuffler.shuffle(input);
};
});
- 모듈에 의존성 배열이 없고, 정의 함수가 최소한 하나 이상의 매개변수를 가질 때에만 CommonJS 모듈로써 작동한다는 점을 유의
궁금한 점 이것저것
🤔 Node.js에서 require로 esm 모듈을 로드할 수 있는 기능을 제공
https://socket.dev/blog/require-esm-backported-to-node-js-20 (opens in a new tab)
- Node.js에서 require로 ESM을 로드할 수 있도록 지원한다는 것은
- 혼합 사용을 권장한다는 의미보다는 듀얼 포맷의 지원을 줄이고, ESM으로 통일해 나가려는 호환성 조치로 보인다.
- 장기적으로는 새로운 모듈은 ESM으로만 작성되고, 점차 CJS를 줄이려는 방향으로 보이기도?
🤔 CJS vs ESM 성능차이?
세민
AMD
- 모듈과 의존성 모두를 비동기적으로 로드할 수 있는 방식
- 높은 유연성
- 코드간 느슨한 결합
- Dojo, MooTools, jQuery 등과 같은 프로젝트에 도입
AMD 모듈 형태
- 모듈 정의부: define 메서드
- 의존성 로딩 처리부: require 메서드
define('myModule', ['dependency1', 'dependency2'], function (dep1, dep2) {
let privateVar = 'private';
// 모듈에 접근가능한 Public API
return {
publicMethod: function () {
return dep1.someMethod() + ' ' + dep2.anotherMethod();
},
getPrivateVar: function () {
return privateVar;
},
};
});
require(['myModule', 'anotherModule'], function (myModule, anotherModule) {
// define 부에서 정의한 Public API 사용
console.log(myModule.publicMethod());
console.log(anotherModule.someFunction());
// 에러 처리
require.onError = function (err) {
console.error('모듈 로드 실패:', err);
};
});
RequireJS 와 curl.js 로 AMD 모듈 사용하기
// RequireJS
require(['app/myModule']);
// 모듈 사용부...
// curl.js
curl(['app/myModule']);
// 모듈 사용부...
jQuery에서 AMD 모듈 사용하기
// jQuery를 의존성으로 가지는 AMD 모듈 정의
define('jqueryModule', ['jquery'], function ($) {
return {
// jQuery를 사용하는 메서드
createElement: function (selector) {
return $(selector);
},
// jQuery 플러그인 확장
initPlugin: function () {
$.fn.myPlugin = function () {
return this.each(function () {
$(this).css('color', 'red');
});
};
},
// jQuery 이벤트 핸들링
setupEvents: function () {
$('.button').on('click', function () {
console.log('Button clicked!');
});
},
};
});
// jQuery 모듈 사용 예시
require(['jquery', 'jqueryModule'], function ($, jqueryModule) {
// 모듈 초기화
jqueryModule.initPlugin();
// jQuery 플러그인 사용
$('.element').myPlugin();
// 이벤트 설정
jqueryModule.setupEvents();
});
- AMD 모듈 방식은 초기 작성해야하는 코드가 좀 있어서 귀찮은듯. 어느정도 형태에 익숙해지면 모듈로서 사용성이 좋다고 느껴짐.
CommonJS
- Server Side 관점에서 모듈화
- Node.js 환경에서 자바스크립트 코드를 패키징하기 위해 처음 등장
- 재사용 가능한 자바스크립트 코드인 외부 의존에 공개할 특정 객체를 내보냄 (exports)
// foo.js
exports.bar = 'hello world';
// main.js
const foo = require('./foo');
console.log(foo.bar); // hello world
Node JS 환경과 브라우저 환경
Common JS는 항상 객체를 내보내기 때문에 조금 더 세밀한 방식으로 컨트롤 하는데는 ES2015, AMD가 더 유리할 수 있다. 또한 ES2015 모듈은 서버에서 Common JS 대안으로 사용이 가능하다.
- require() 함수를 호출하면 항상 Common JS 모듈 로더
- import() 함수를 호출하면 항상 ECMAScript 모듈 로더
이는 package.json 파일에 설정된 type과 상관 없이 항상 적용된다.
UMD
- AMD와 CommonJS를 모두 지원하는 모듈 시스템
- 브라우저와 Node.js 환경 모두에서 동작하도록 설계
- 모듈 시스템을 감지하여 적절한 방식으로 동작
// UMD 패턴 예시
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD 환경
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS 환경
module.exports = factory(require('jquery'));
} else {
// 브라우저 전역 환경
root.myModule = factory(root.jQuery);
}
})(this, function ($) {
// 모듈 구현부
return {
sayHello: function () {
console.log('Hello from UMD module!');
},
};
});
UMD의 주요 특징
-
환경 감지
- AMD 환경:
define
함수 존재 여부 확인 - CommonJS 환경:
exports
객체 존재 여부 확인 - 브라우저 환경: 전역 객체에 모듈 노출
- AMD 환경:
-
장점
- 다양한 환경에서 동작 가능
- 기존 모듈 시스템과의 호환성
- 라이브러리 배포에 적합
-
단점
- 코드가 복잡해질 수 있음
- 번들러 사용 시 불필요한 코드가 포함될 수 있음