Node.js의 모듈은 복잡한 어플리케이션을 구성하기 위한 블록 역할을 하기도 하나, 명시적으로 익스포트가 표시되지 않은 모든 내부적인 함수와 변수들을 비공개로 유지하여 정보를 숨기는 중요한 매커니즘이기도 하다.

노출식 모듈 패턴

Javascript의 주요 문제점 중 하나는 네임스페이스가 없다는 것이다. 전역 범위에서 실행 되는 프로그램은 내부 어플리에키션과 종속된 라이브러리 코드의 데이터들로 인해 충돌이 발생 될 수 있다. 이 문제를 해결 하기 위한 보편적인 기법을 노출식 모듈 패턴이라고 한다.

const module = (() => {
	const privateFoo = () => {...};
	const privatbar = [];

	const exported = {
		publicFoo: () => {...},
		publicBar: () => {...}
	}
	return exported;
	})();

이 패턴은 private 범위를 만들고 공개될 부분만 익스포트하는 방식이다. 이떄에 반환되는 리턴 값은 exported의 값들이며 나머지 모듈 내부 콘텐츠들은 실제로 외부에서 액세스 할 수 없다. 사실상 이 패턴의 사상은 Node.js 모듈 시스템의 기반으로 사용된다.

CommonJS 모듈

CommonJS는 자바스크립트 생태계를 표준화하려는 목표를 가진 그룹으로, 가장 많이 사용되는 제안 중 하나로 CommonJS 모듈이 있다. Node.js 사용자 정의 확장을 추가하여 이 스펙 위에 모듈 시스템을 구축 했다. 각 모듈이 private 범위에서 실행되어 로컬로 정의된 모든 변수가 전역의 네임스페이스와 충돌하지 않는다는 점에서 노출식 모듈 패턴이 어떻게 작동하는지를 유추할 수 있다.

function loadModule (filename, module, require){
	const wrappedSrc = (module, exports, require) => {
		fs.readFileSync(filename, 'utf-8')
	}(module, module.exports, require) // 해당 모듈들로 위 함수의 인자들을 초기화 한다.
	eval(wrappedSrc)
}

const require = (moduleName) => {
	const id = require.resolve(moduleName)
	if(require.cache[id]){ return require.cache[id].exports}
	}

const module = {
	exports: {},
	id
}

require.cache[id] = module;
loadmodule(id, module, require);

return module.exports; }

require.cache = {};
require.resolve = (moduleName) => {}

위 함수는 모듈을 로드하는데 사용하는 Node.js의 require()함수의 동작을 모방하는 코드이다. 물론 실제 require()함수 내부 동작을 정확하게 반영하고 있지는 않다. 그러나 어떻게 모듈이 정의되고 로드되는지 이해하는데는 부족함이 없다. 위 모듈 시스템을 설명하면 아래와 같다.

module.exports vs exports

Node.js에 익숙하지 않은 많은 개발자들에게 있어 public API를 공개하기 위해 사용하는 exports와 module.exports의 차이점을 이해하는 것은 매우 어렵다. exports는 module.exports의 초기 값에 대한 참조일 뿐인데, exports는 초기값을 참조하기 때문에 새로운 속성을 추가 할 수 가 있다.