모듈이란? - 애플리케이션을 구성하는 개별적 요소이자, 재사용 가능한 코드 조각을 말합니다.
모듈은
IIFE 즉시 실행 함수 표현(IIFE, Immediately Invoked Function Expression)
→ 정의되자마자 즉시 실행되는 함수를 말합니다.
*IIFE로 모듈 패턴 만들기 - 출처 IIFE라는 이름을 지은 Bem Almen의 블로그(2010년)
1var counter = (function(){
2 var i = 0;
3
4 return {
5 get: function(){
6 return i;
7 },
8 set: function( val ){
9 i = val;
10 },
11 increment: function() {
12 return ++i;
13 }
14 };
15 }());
16
17 // `counter` is an object with properties, which in this case happen to be
18 // methods.
19
20 counter.get(); // 0
21 counter.set( 3 );
22 counter.increment(); // 4
23 counter.increment(); // 5IIFE로 모듈을 만들어 사용함으로 얻었던 이점
IIFE로 모듈을 만들어 썼을 때의 아쉬운점
IIFE를 통한 모듈 패턴이 사용되던 시기에 JavaScript는 주로 브라우저 환경에서 사용되었는데요.
서버 측 JavaScript 환경이 등장하면서(Node.js - 2009년 5월 27 처음 등장) 더 나은 모듈 시스템의 중요성이 더욱 커졌습니다. 이 때 등장한 것이 CommonJS 모듈 시스템입니다.
JavaScript를 서버 측에서 사용할 수 있는 표준을 만들기 위해 모인 그룹 ServerJS에 의해 시작되었습니다.
CommonJS는 동기적으로 모듈을 로드합니다. 서버 측 환경에서는 동기식 로딩이 자연스럽기 때문입니다.
왜 그럴까요?
문법
require 함수로 모듈을 로드하고, module.exports 또는 exports객체로 모듈을 내보냅니다.
1//모듈 정의하기
2const message = "Hello, CommonJS";
3
4function greet() {
5 console.log(message);
6}
7
8module.exports = {
9 greet
10};//모듈 가져오기
const example = require('./example');
example.greet();특징
파일 단위 모듈 - 각 파일이 하나의 모듈로 간주되며, 모듈 간의 의존성은 파일 시스템 경로로 관리됩니다.
캐싱 - 모듈은 처음 로드될 때 한 번만 실행되고, 이후에는 캐시된 버전이 사용됩니다.
장단점
장점 - 단순한 문법, 동기식 로딩, 풍부한 생태계(npm)
단점 - 클라이언트 측 환경에 부적합(동기식 로딩이어서)
*CommonJS 모듈 시스템에서 Factory 패턴
CommonJS 모듈 시스템에서 Factory 패턴은 module.exports를 통해 모듈을 정의하고, require 함수로 모듈을 로드할 때 사용됩니다. CommonJS에서는 각 파일이 자체적으로 하나의 모듈로 간주되며, module.exports는 모듈을 내보내는 Factory 역할을 합니다.
1var add = function(x, y) {
2 return x + y;
3};
4
5var subtract = function(x, y) {
6 return x - y;
7};
8
9module.exports = {
10 add: add,
11 subtract: subtract
12};var math = require('./math');
console.log(math.add(2, 3)); // 출력: 5
console.log(math.subtract(5, 2)); // 출력: 3브라우저 환경을 지원하는 비동기식 로딩 모듈시스템에 대한 필요성 대두되었습니다. ⇒ AMD의 등장
비동기식 로딩을 지원하는 브라우저 환경에 적합한 모듈 시스템
브라우저에서는 왜 비동기식 로딩이 적합한가요?
문법
RequireJS라는 AMD 규격을 따르는 모듈 로더 라이브러리를 사용합니다.
*RequireJS는 파일 경로 매핑, 템플릿 로딩, 비동기적 의존성 로딩 등을 지원합니다.
define 함수로 모듈을 정의하고, require 함수로 모듈을 로드하여 사용합니다.
1// math.js
2define([], function() {
3 var add = function(x, y) {
4 return x + y;
5 };
6
7 var subtract = function(x, y) {
8 return x - y;
9 };
10
11 return {
12 add: add,
13 subtract: subtract
14 };
15});// main.js
require(['math'], function(math) {
console.log(math.add(2, 3));
console.log(math.subtract(5, 2));
});장점
단점
문법이 상대적으로 복잡하고 많은 함수 호출과 콜백이 필요해 코드가 장황해집니다.
! CommonJS 모듈과 직접 호환되지 않습니다. (호환성 문제)
⇒ 호환성 문제 해결을 위해 UMD 등장
AMD 모듈 로더 라이브러리인 RequireJS의 define 함수
define 함수는 다음과 같은 구조를 가집니다.
define(id, dependencies, factory);id (optional): 모듈의 이름을 지정합니다. 생략하면 파일 이름이 모듈 이름으로 사용됩니다.dependencies (opitional): 모듈이 의존하는 모듈들의 배열입니다. 생략하면 기본적으로 require, exports, module이 사용됩니다.factory: 모듈의 실제 구현입니다. 함수나 객체를 반환합니다.*AMD의 facotry패턴
객체 생성의 책임을 팩토리 클래스나 메서드에 위임하는 디자인 패턴입니다. 객체 생성의 복잡성을 숨기고, 객체 생성 로직을 중앙 집중화합니다.
define함수의 factory parameter
모듈을 정의하고 반환하는 역할을 합니다. 함수는 주어진 의존성들을 받아 모듈의 인터페이스를 정의하고 이를 호출하는 코드에 반환합니다.
1// math.js 모듈 정의
2define('math', [], function() {
3 var add = function(x, y) {
4 return x + y;
5 };
6
7 var subtract = function(x, y) {
8 return x - y;
9 };
10
11 return {
12 add: add,
13 subtract: subtract
14 };
15});factory파라미터로 전달된 함수는 의존성 배열을 통해 모듈의 의존성을 주입받고, 모듈을 정의하여 반환합니다.
이 함수는 팩토리 함수로서의 역할을 합니다. (의존성을 주입받아 모듈을 생성하고 이를 호출하는 코드에 반환합니다.) → 모듈 간의 의존성을 명확히 하고, 모듈의 재사용성과 유지보수성을 높이는데 기여합니다.
AMD의 호환성 문제로 인해 UMD가 등장하는데… (이어서)