Notice
Recent Posts
Recent Comments
Link
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
Tags more
Archives
Today
Total
관리 메뉴

꾸르빅 블로그

[디자인 패턴 바이블] 2장. 모듈 시스템 본문

도서/Node.js

[디자인 패턴 바이블] 2장. 모듈 시스템

꾸르빅 2023. 3. 5. 03:03

들어가기에 앞서 Key Point

 - 모듈이 왜 필수적이며 Node.js에서 다른 모듈 시스템이 가능한 이유

 - Node.js는 두가지 모듈 시스템(CommonJs , ECMAScript module)을 사용하는데 이 두가지 형태가 왜 존재하는지

2-1. 모듈과 모듈 시스템

 - 모듈 시스템(Module System) : 모듈을 정의하고 사용할 수 있게 해주는 도구이자 문법

 - 모듈(Module) : 소프트웨어의 실제 유닛

 - 좋은 모듈 시스템은 아래와 같은 특징이 있다.

  1) 코드베이스 분할 방법 제시 : 구조적으로 관리할 수 있으며 독립적인 기능의 조각으로 개발할 수 있도록 가이드

  2) 코드의 재사용 : 기능을 간단하고 구조화하여 다른 프로젝트에서 사용가능하도록 개발

  3) 은닉성 : 복잡한 구현을 숨기고 간단한 인터페이스를 노출

  4) 종속성 관리 : 모듈 실행 시 필요한 종속성들을 쉽게 import할 수 있도록 관리 

 

2-2. Javascript, Node.js에서의 모듈 시스템

 - Javascript는 초반에 내장 모듈시스템 없이 코드베이스 분할(1번항목), <script>태그를 통한 쉬운 import(4번항목)가 가능했으나,  Javascript 브라우저 애플리케이션이 점점 복잡해지고 여러 프레임워크가 사용되면서 Javascript 프로젝트에 효율적으로 사용될 모듈 시스템 정의가 필요했다. 이에 성공적 결과를 낸 것이 AMD와 UMD이다.

    ※ AMD(Asynchronous Module Definition) : 비동기 모듈에 대한 표준안. 브라우저는 모든 모듈이 다 로딩될 때까지 기다릴 수 없기 때문에 비동기 모듈 로딩 방식으로 구현되어있다. RequireJS에 의해 대중화되었다.

    ※ UMD(Universal Module Definition) : CommonJS와 AMD를 통합하기 위한 표준안.

 - Node.js는 운영체제의 파일 시스템에 직접적으로 접근할 수 있기 때문에, 브라우저가 아닌 환경에서 Javascript 모듈을 제공할 수 있도록 고안된 CommonJS가 탄생하였다. 이후 Browserify와 Webpack과 같은 모듈 번들러 덕분에 브라우저 환경에서도 사용하게 되었다.

 - 이후 2015년도에 ES6의 발표와 함께 표준 모듈 시스템(ESM)의 제안이 나왔다. 이에 따른 구체적인 구현을 제공하지 않았기 때문에 Node.js 13.2 버전부터 ESM에 대한 안정적인 지원을 한다. 따라서 현재는 CommonJS가 주된 모듈 시스템이라고 볼 수 있다.

 

2-3. 모듈 시스템과 패턴

2-3-1. 노출식 모듈 패턴

 - 은닉의 필요성 : Javascript는 네임스페이스가 없어 모든 스크립트가 전역 범위에서 실행된다. 따라서 애플리케이션 코드나 종속성 라이브러리가 노출된다면 스코프가 오염되어 코드 충돌이 날 수 있다. 또한 애플리케이션이 확장됨에 따라 개별적이고 분리된 기능 구현이 필요하다.

 - 노출식 모듈 패턴(revealing module pattern) : 자기 호출 함수(즉시 실행 함수 표현-IIFE)를 사용하여 private 범위를 만들고 공개될 부분만 내보내는 방식.

const myModule = (() => {
	const privateFoo = () => {}
    const privateBar = []
    
    const exported = {
    	publicFoo: () => {},
        publicBar: () => {}
    }
    return exported
})() //괄호 파싱 시 함수 호출
console.log(myModule) // publicFoo와 publicBar로 구성된 객체 반환
console.log(myModule.privateFoo, myModule.privateBar) // undefined 반환

 

2-4. CommonJS 모듈

- CommonJS 명세의 두가지 주요 개념 요약

  1) require는 로컬 파일 시스템으로부터 모듈을 import하게 해준다.

  2) exports와 module.exports는 특별한 변수로서 현재 모듈에서 공개될 기능들을 내보내기 위해서 사용된다.

2-4-1. 직접 만드는 모듈 로더

 - 노출식 모듈 패턴을 이용한 Node.js require() 함수 모방

function loadModule(filename, module, require) {
	const wrappedSrc = 
    	'(function (module, exports, require) {
        	${fs.readFileSync(filename, 'UTF8')}
        })(module, module.exports, require)'  //module.exports 사용 주의
    eval(wrappedSrc)
}
function require(moduleName) {
	const id = require.resolve(moduleName) //모듈 전체 경로 로드
    if(require.cache[id]) { //캐싱되어 있는 경우 캐시값을 가져옴
    	return require.cache[id].exports
    }
    
    //모듈 메타 데이터: 최초 로드를 위해 exports 속성 초기화된 module 객체 생성
    const module = {
    	exports: {}, //로드한 모듈 코드에서의 public API export용
        id
    }
    require.cache[id] = module //캐시 업데이트
    loadModule(id, module, require) //모듈소스코드를 읽어와 module.exports 객체에 public API를 넣음
	
    return module.exports
}
require.cache = {}
require.resolve = (moduleName) => {
 /* 모듈 이름으로 모듈의 전체 경로를 찾아냄 */
}

2-4-2. 모듈 정의

 - module.export 변수에 할당되지 않다면 비공개

const dependency = require('./anotherModule')

//private함수
function log() {
	console.log('Well done ${dependency.username}')
}

//공개적으로 사용되기 위해 익스포트되는 API
module.exports.run = () => {
	log()
}

2-4-3. module.exports  VS  exports

 - module.exports는 require에서 사용되는 return 값이며, exports는 module.exports의 참조값이다.

따라서, export의 재할당은 module.exports에 영향을 미치지 않는다. exports 사용 시 새로운 속성(property)을 추가할 수 있다

const module = { exports: {} }
const exports = module.exports

exports.hello = () => {
	console.log('Hello')
}

module.exports = () => {
	consloe.log('Hello')
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

참고자료

 

Comments