어제 Node.js 모듈의 순환(cycle)을 공부하다가 모르는 부분이 생겼다. (TIL 016 참고.)
어떤 폴더에 다음의 세 스크립트 파일이 있다고 하자.
// a.js
console.log('a starting');
exports.done = false;
const b = require('./b.js'); // 1. b.js를 불러온다
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
// b.js
console.log('b starting');
exports.done = false;
const a = require('./a.js'); // 2. a.js를 불러온다
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');
// main.js
console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done = %j, b.done = %j', a.done, b.done);
그리고 main.js를 실행하면 다음과 같은 순서로 실행된다.
const a = require('./a.js');
을 실행한다.require()
한다.require()
한다.require()
한다. 이렇게 나를 호출한 스크립트를 내가 다시 호출하여 무한 반복하게 하는 패턴을 순환(cycle)이라고 한다.
Node.js에서는 이러한 순환 문제를 막기 위해, 나를 호출한 스크립트를 내가 다시 호출할 경우, 실제로 해당 스크립트를 다시 호출하지 않고 불완전한 복사본을 반환하도록 하였다.
a.js는 스크립트가 실행되다가 require(b.js)
를 마주치자, b.js 모듈을 가져오기 위해 잠시 실행을 멈춘 상태이다.
그리고 b.js에서 자신을 부른 a.js를 require()
하면, a.js는 다시 호출되어 처음부터 로딩되는 것이 아니라, b.js를 실행하느라 잠시 중단된 a.js의 exports 모듈을 리턴한다. 불완전한 모듈이긴 하지만 모듈을 바로 리턴하기 때문에 순환 문제를 해결할 수 있다.
그런데, 이해가 안가는 점이 있었다.
실제로 main.js를 실행하면 콘솔에는 다음과 같이 찍힌다.
$ node main.js
main starting
# const a = require('./a.js'); 실행
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
# const b = require('./b.js); 실행
# console.log('in main, a.done = %j, b.done = %j',...); 실행
in main, a.done = true, b.done = true
const a = require('./a.js');
를 호출한 결과는 정상적으로 나오는데, 그 다음 구문인 const b = require('./b.js');
를 실행한 결과는 나오지 않았다.
이는 require.cache
때문이다. node를 실행하는 도중 require()
되는 모듈은 모두 require.cache
라는 객체에 캐싱된다. node를 시작하고 한번 require()
된 모듈이 다시 require()
될 때는 다시 실행되지 않고 require.cache
객체에 캐싱된 값을 가져다 쓴다.
예제에서는 a.js에서 b.js를 호출했기 때문에 a.js 호출을 완료한 시점에서는 require.caceh
객체에 b.js 모듈이 캐싱되어 있다. 따라서 const b = require('./b.js');
구문이 실행될 때는 b.js를 호출하지 않고 캐싱된 값을 가져온다.
정리하자면, node에서 스크립트가 처음 require()
될 때는 해당 스크립트를 한 번 실행하고, 그 결과가 module.cache
객체에 캐싱된다. 그리고 해당 스크립트가 또 require()
되면 스크립트를 실행하지 않고 module.cache
객체에 캐싱된 값을 가져다 쓴다.