프로그래밍 언어/NODE JS

module, exports, require

· 코딩마이데이

var.js

exports.odd = "홀수입니다";
exports.even = "짝수입니다";

 

 

index.js

const { odd, even } = require("./var");
const checkNumber = require("./func");

function checkStringOddOrEven(str) {
  if (str.length % 2) {
    // 홀수만
    return odd;
  }
  return even;
}

console.log(checkNumber(10));
console.log(checkStringOddOrEven("hello"));

 

콘솔

$ node index
짝수입니다
홀수입니다

 

module.exports로 한 번에 대입하는 대신, 각각의 변수를 exports 객체에 하나씩 넣었습니다. 동일하게 동작하는 이유는 module.exports와 exports가 같은 객체를 참조하기 때문입니다. 실제로 console.log(module.exports === exports)를 하면 true가 나옵니다. 따라서 exports 객체에 add 함수를 넣으면 module.exports에도 add 함수가 들어갑니다.

exports와 module.exports의 관계

 

이번에는 모듈을 불러오는 require에 대해 알아봅시다. require는 함수이고, 함수는 객체이므로 require는 객체로서 몇 가지 속성을 갖고 있습니다. 그중에서 require.cache와 require,main에 대해 알아보겠습니다.

var.js가 있는 곳에 require.js를 만듭니다.

console.log("require가 가장 위에 오지 않아도 됩니다.");

module.exports = "저를 찾아보세요";

require("./var");

console.log("require.cache입니다.");
console.log(require.cache);
console.log("require.main입니다.");
console.log(require.main === module);
console.log(require.main.filename);

 

콘솔

$ node require
require가 가장 위에 오지 않아도 됩니다.
require.cache입니다.
[Object: null prototype] {
  'D:\\공부\\Nodejs\\3-4\\require.js': {
    id: '.',
    path: 'D:\\공부\\Nodejs\\3-4',
    exports: '저를 찾아보세요',
    filename: 'D:\\공부\\Nodejs\\3-4\\require.js',
    loaded: false,
    children: [ [Object] ],
    paths: [
      'D:\\공부\\Nodejs\\3-4\\node_modules',
      'D:\\공부\\Nodejs\\node_modules',
      'D:\\공부\\node_modules',
      'D:\\node_modules'
    ],
    [Symbol(kIsMainSymbol)]: true,
    [Symbol(kIsCachedByESMLoader)]: false,
    [Symbol(kIsExecuting)]: true
  },
  'D:\\공부\\Nodejs\\3-4\\var.js': {
    id: 'D:\\공부\\Nodejs\\3-4\\var.js',
    path: 'D:\\공부\\Nodejs\\3-4',
    exports: { odd: '홀수입니다', even: '짝수입니다' },
    filename: 'D:\\공부\\Nodejs\\3-4\\var.js',
    loaded: true,
    children: [],
    paths: [
      'D:\\공부\\Nodejs\\3-4\\node_modules',
      'D:\\공부\\Nodejs\\node_modules',
      'D:\\공부\\node_modules',
      'D:\\node_modules'
    ],
    [Symbol(kIsMainSymbol)]: false,
    [Symbol(kIsCachedByESMLoader)]: false,
    [Symbol(kIsExecuting)]: false
  }
}
require.main입니다.
true
D:\공부\Nodejs\3-4\require.js

 

require가 반드시 파일 최상단에 위치할 필요가 없고, module.exports도 최하단에 위치할 필요가 없다는 것입니다. 아무 곳에서나 사용해도 됩니다.

require.cache 객체에 대해 require.js나 var.js 같은 파일 이름이 속성명으로 들어 있는 것을 볼 수 있습니다. 속성 값으로는 각 파일의 모듈 객체가 들어 있습니다. 한 번 require한 파일은 require.cache에 저장되므로 다음 번에 require 할 때는 새로 불러오지 않고 require.cache에 있는 것이 재사용됩니다.

만약 새로 require하길 원하다면, require.cache의 속성을 제거하면 됩니다. 다만, 프로그램의 동작이 꼬일 수 있으므로 권장히지 않습니다. 속성을 자세히 살펴보면, module.exports했던 부분(exports)이나 로딩 여부(loaded), 부모(parent), 자식(children) 모듈 관계를 찾을 수 있습니다.

require.main은 노드 실행 시 첫 모듈을 가리킵니다. 현재 node.require로 실행했으므로 require.js가 require,main이 됩니다. require.main 객체의 모양은 require.cache의 모듈 객체와 같습니다.

 

dep1.js

const dep2 = require("./dep2");
console.log("require dep2", dep2);
module.exports = () => {
  console.log("dep2", dep2);
};

 

dep2.js

const dep1 = require("./dep1");
console.log("require dep1", dep1);
module.exports = () => {
  console.log("dep1", dep1);
};

 

dep-run.js를 만들어 두 모듈을 실행해보겠습니다.

const dep1 = require("./dep1");
const dep2 = require("./dep2");

dep1();
dep2();

 

코드가 위애서 실행되므로 require('./dep1')이 실행됩니다. dep1.js에서는 제일 먼저 require('./dep2)가 실행됩니다. 다시 dep2.js에서는 require('./dep1')이 실행됩니다.

 

콘솔

$ node dep-run
require dep1 {}
require dep2 [Function (anonymous)]
dep2 [Function (anonymous)]
dep1 {}
(node:30180) Warning: Accessing non-existent property 'Symbol(nodejs.util.inspect.custom)' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:30180) Warning: Accessing non-existent property 'constructor' of module exports inside circular dependency
(node:30180) Warning: Accessing non-existent property 'Symbol(Symbol.toStringTag)' of module exports inside circular dependency
(node:30180) Warning: Accessing non-existent property 'Symbol(nodejs.util.inspect.custom)' of module exports inside circular dependency
(node:30180) Warning: Accessing non-existent property 'constructor' of module exports inside circular dependency
(node:30180) Warning: Accessing non-existent property 'Symbol(Symbol.toStringTag)' of module exports inside circular dependency

 

dep1의 module.exports가 함수가 아니라 빈 객체로 표시됩니다. 이러한 현상을 순환 참조(circular dependency)라고 부릅니다. 이렇게 순환 참조가 있을 경우에는 순환 참조되는 대상을 빈 객체로 만듭니다. 이때 에러가 발생하지 않고 조용히 빈 객체로 변경되므로 예기치 못한 동작이 발생할 수 있습니다. 따라서 순환 참조가 발생하지 않도록 구조를 잘 잡는 것이 중요합니다.

'프로그래밍 언어 > NODE JS' 카테고리의 다른 글

OS  (0) 2025.03.26
process  (0) 2025.03.23
__filename, __dirname  (0) 2025.03.17
타이머  (0) 2025.03.13
console  (0) 2025.03.12