모듈이란?
프로그램 내부를 기능별 단위로 분할한 부분
모듈 분리 시 장점
- 유지보수
- 기능들이 모듈화가 잘 되어있다면 의존성을 줄일 수 있어 유지보수가 편하다.
- 네임스페이스화
- 모듈 분리를 하면 모듈만의 네임스페이스를 갖기 때문에 중복 변수명으로부터 자유로워진다.
- 재사용성
- 재사용 가능한 로직을 모듈로 분리시켜 필요할 때마다 사용할 수 있다.
모듈 시스템
JS 프로그램을 모듈로 개발하고 배포할 수 있게 하기 위해 만들어짐
CJS(Common JS)
- 서버 사이드에서 사용, 동기적으로 작동
- Browserify
모듈화 방법
- 스코프(Scope): 모든 모듈은 자신만의 독립적인 실행 영역이 있어야 한다.
- 정의(Definition): 모듈 정의는 exports 객체를 이용한다.
- 사용(Usage): 모듈 사용은 require 함수를 이용한다.
// add.js
function add (a, b) {
return a + b;
}
module.exports = add;
// index.js
const add = require('./add');
console.log(add(4, 5));
//9
AMD(Asynchronous Module Definition)
- 클라이언트 사이드에서 주로 사용(서버 사이드에서도 사용 가능함), 비동기적으로 작동
- define - require 문법 사용
- 특징: 동적 로딩, 의존성 관리, 모듈화
define() 함수
Javascript는 파일 스코프가 따로 존재하지 않으므로 define() 함수로 네임스페이스 역할을 하여 모듈 내부에서 사용하는 변수와 전역 변수를 분리한다.
define(id?, dependencies?, factory);
- id
- 모듈을 식별하는데 사용하는 인수
- id가 존재하지 않은 경우 로더가 요청하는 <script> 태그의 src 값을 기본 id로 설정함
- dependencies
- 모듈의 의존성을 나타내는 배열, 반드시 먼저 로드되어야 하는 모듈을 나타냄
- 세번째 인수인 factory로 전달
- 기본값은 ["require", "exports", "module"]
- factory
- 모듈이나 객체를 인스턴스화하기 위해 실행해야하는 함수
- factory가 함수일 경우 한 번만 실행되어야 함, return 값이 있으면 exports 객체의 속성값으로 할당
- factory가 객체인 경우 exports 객체의 속성값으로 할당
// id가 'alpha'인 모듈이 beta라는 모듈 사용한다고 정의
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
exports.verb = function() {
return beta.verb();
//Or:
return require("beta").verb();
}
});
// 객체 반환하는 익명 모듈
// 모듈이 정의된 파일의 경로가 자동으로 식별자로 지정됨
define(["alpha"], function (alpha) {
return {
verb: function(){
return alpha.verb() + 2;
}
};
});
// 의존성 없는 익명 모듈
define({
add: function(x, y){
return x + y;
}
});
// commonJS 래핑을 사용한 모듈
define(function (require, exports, module) {
var a = require('a'),
b = require('b');
exports.action = function () {};
});
UMD
- CommonJS와 AMD 모두 사용하기 위한 구현 패턴
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], 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();
} else {
// Browser globals (root is window)
root.returnExports = factory();
}
}(typeof self !== 'undefined' ? self : this, function () {
// 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 {};
}));
모듈 선언을 위해 IIFE(즉시실행함수) 패턴을 사용한다.
각 환경에서 모두 모듈 사용할 수 있도록 한다.
- AMD에서 사용하는 define 함수를 사용할 수 있는지 확인
- AMD를 지원하지 않는다면 commonJS 모듈을 사용할 수 있는지 확인
- 둘다 아니라면 브라우저라고 판단
ESM(ES Modules)
- ES6 부터 지원하고 있는 표준 모듈 시스템
- export - import 문법 사용
- 브라우저에서는 script 태그 type="module" 필요
- nodejs에서는 package.json에 "type":"module" 추가
동작 과정
디펜던시 그래프가 그려지고 entry point로 사용하는 파일에서 import 문을 따라 나머지 파일들을 찾는다.
브라우저에서 파일 자체를 사용할 수 없기 때문에 모듈 레코드라는 데이터 구조로 변환해야하는데, 이 과정에서 파일들의 모든 구문을 분석해야 한다.
그 다음 모듈 레코드를 모듈 인스턴스로 전환한다.
모듈 인스턴스는 코드와 상태를 결합한 것이다. 여기서 상태는 메모리에 있으므로 인스턴스화 단계에서 메모리에 연결한다.
- 구성(construction)
- 브라우저 로더를 사용해 entry point부터 시작하여 모든 모듈 파일을 로드, 파싱하여 모듈 레코드로 변환
- 모듈이 포함된 파일을 다운로드할 위치 파악(module resolution)
- 파일 가져오기(URL을 통해 다운로드 or 파일 시스템에서 불러오기)
- 파일을 모듈 레코드로 구문 분석
- 모듈 레코드들을 모듈 맵에 추가
- 브라우저 로더를 사용해 entry point부터 시작하여 모든 모듈 파일을 로드, 파싱하여 모듈 레코드로 변환
- 인스턴스화(instantiation)
- 모듈 변수 인스턴스들을 메모리에 연결(linking)
- JS 엔진이 모듈 레코드의 변수를 관리하는 모듈 환경 레코드(module environment record) 생성
- 모든 export된 값을 배치하기 위한 메모리 공간을 찾음
- 모듈 하위의 모든 export 연결을 마무리하면 해당 모듈에서 import 한 항목들을 export들과 연결함
- export와 import는 같은 메모리의 주소를 가리킴(라이브 바인딩)
- 모듈 변수 인스턴스들을 메모리에 연결(linking)
읽기 전용 라이브 바인딩(readonly binding)
export 한 모듈에서 값을 변경하면 해당 변경 내용이 import 한 모듈에 표시된다.
값을 export 하는 모듈은 언제든지 값을 변경할 수 있지만, import 하는 모듈은 가져온 값을 변경할 수 없음
CJS에서는 전체 export 객체가 내보낼 때 복사된다. 그러므로 export하는 모듈이 값을 변경해도 import하는 모듈은 변경사항을 알 수 없다.
-
- 각 모듈 레코드는 디펜던시가 걸려 있는 다른 모듈 레코드를 가리킴
- 메모리 공간을 찾고 지정할 뿐 실제 값을 메모리에 채우진 않음
3. 평가(Evaluation)
- 코드를 실행하여 메모리를 변수의 실제 값으로 채움
- module environment record가 가리키는 메모리 영역에 실제 인스턴스를 만듦
- 모듈은 한 번만 평가함
참고
https://d2.naver.com/helloworld/12864
https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/
'JavaScript' 카테고리의 다른 글
[JavaScript] 연산자 정리 (0) | 2022.07.15 |
---|---|
용어 정리 (0) | 2022.07.01 |