Node.js 교과서


모듈이란?

특정한 기능을 하는 함수나 변수들의 집합을 말한다.

모듈은 자체로도 하나의 프로그램이면서 다른 프로그램의 부품으로도 사용할 수 있다. 자바스크립트에서 코드를 재사용하기 위해 함수로 만드는 것과 비슷하다.

노드에서는 CommonJS, ECMAScript 두 가지 모듈을 사용한다.

CommonJS 모듈

CommonJS 모듈은 표준 자바스크립트 모듈은 아니지만 표준이 나오기 이전부터 쓰였기 때문에 노드 생태계에서 가장 널리 쓰이는 모듈이다.

module.exports

var.js에 변수 두 개를 선언하고 module.exports에 변수들을 담은 객체를 대입했다.

// var.js
const odd = '홀수';
const even = '짝수';

module.export = {
  odd,
  even,
};

이제 이 파일은 변수들을 모아둔 모듈로서 다른 파일에서 이 파일을 불러오면 module.exports에 대입된 값을 사용할 수 있다.

다른 파일에서 require 함수 안에 불러올 모듈의 경로를 기입해 모듈을 불러올 수 있다.

const { odd, even } = require('./var');

function checkOddOrEven(num) {
  if(num % 2){
    return odd; 
  }
  return even
}

module.exports = checkOddOrEven;

이렇게 다른 모듈을 사용하는 파일도 다시 모듈로 만들 수 있다. 또한 module.exports 에는 객체만 대입해야 하는 것은 아니며 함수나 변수를 대입해도 된다.

exports

module.exports 외에도 exports객체로도 모듈을 만들 수 있다.

  exports.odd = '홀수';
  exports.even = '짝수';

module.exports로 한 번에 대입하는 대신 각각의 변수를 exports 객체에 하나씩 넣어는데도 둘은 동일하게 동작한다.
그 이유는 module.exports === exports 이기 때문이다.

require()

require는 함수이고, 함수는 객체이므로 속성을 몇 개 갖고 있다.
그중에서 require.cacherequire.main을 살펴보자.

// /root
//    └─ var.js
//    └─ require.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);

실행 결과 >

require가 가장 위에 오지 않아도 된다.
{ odd: 'CJS 홀수1입니다.', even: 'CJS 짝수1입니다.' }
require.cache입니다.
[Object: null prototype] {
  'D:\\바탕화면\\study\\node.js\\require.js': {
    id: '.',
    path: 'D:\\바탕화면\\study\\node.js',
    exports: '저를 찾아보세요',
    filename: 'D:\\바탕화면\\study\\node.js\\require.js',
    loaded: false,
    children: [ [Object] ],
    paths: [
      'D:\\바탕화면\\study\\node.js\\node_modules',
      'D:\\바탕화면\\study\\node_modules',
      'D:\\바탕화면\\node_modules',
      'D:\\node_modules'
    ]
  },
  'D:\\바탕화면\\study\\node.js\\var.js': {
    id: 'D:\\바탕화면\\study\\node.js\\var.js',
    path: 'D:\\바탕화면\\study\\node.js',
    exports: { odd: 'CJS 홀수1입니다.', even: 'CJS 짝수1입니다.' },
    filename: 'D:\\바탕화면\\study\\node.js\\var.js',
    loaded: true,
    children: [],
    paths: [
      'D:\\바탕화면\\study\\node.js\\node_modules',
      'D:\\바탕화면\\study\\node_modules',
      'D:\\바탕화면\\node_modules',
      'D:\\node_modules'
    ]
  }
}
require.main입니다.
true
D:\바탕화면\study\node.js\require.js

require는 반드시 파일 최상단에 위치할 필요가 없고,
module.exports도 최하단에 위치할 필요가 없다.

require.cache 객체에 require.jsvar.js 같은 파일 이름이 속성명으로 들어 있다. 속성값으로는 각 파일의 모듈 객체가 들어 있다. 한번 require한 파일은 require.cache에 저장되므로 새로 불러오지 않고 재사용이 가능하다.

require.main은 노드 실행 시 첫 모듈을 가리킨다. 현재 node require 로 실행했으므로 require.jsrequire.main이 된다.

ECMAScript 모듈

ECMAScript 모듈(ES모듈)은 공식적인 자바스크립트 모듈 형식이다.

// var.mjs
export const odd = '홀수';
export const even = '짝수';
// func.mjs

import { odd, even } from './var.mjs';

function checkOddOrEven(num) {
  if(num % 2) { // 홀수이면
    return odd;
  }
  return even;
}

export default checkOddOrEven

.js 확장자에서 import를 사용하면 SyntaxError: Cannot use import statement outside a module 에러가 발생한다. .js확장자를 사용하면서 ES 모듈을 사용하려면 package.jsontype: "module"속성을 넣으면 된다.

CommonJS 모듈과는 다르게 import 시 파일 경로에서 .js, .mjs같은 확장자는 생략할 수 없다.

브라우저

브라우저
require

다이내믹 임포트

조건부로 모듈을 불러오는 것을 다이내믹 임포트라고 한다.

const a = false;
if(a) {
  require('./func);
}
console.log('성공');

하지만 ES모듈은 조건문 안에서 import하는 것이 불가능하다.

// dynamic.mjs
const a = true;
if(a) {
  import './func.mjs;' // SyntaxError: Unexpected string
}
console.log('성공');

대신 import() 함수를 사용해 모듈을 동적으로 불러올 수 있다.

// dynamic.mjs
const a = true;
if(a) {
  const m1 = await import('./func.mjs;');
  console.log(m1);
}
$ node dynamic.mjs
 [Module: null prototype] { default: [Function: checkOddOrEven] }

import()는 Promise를 반환 하기 때문에 await이나 then을 붙여야 한다. 위 코드에서는 async 함수를 사용하지 않았는데, ES 모듈의 최상위 스코프에서는 async함수 없이도 await할 수 있다.

__filename, __dirname

노드에서는 파일 사이에 모듈 관계가 있는 경우가 많으므로 현재 파일의 경로나 파일명을 알아야 하는 경우가 있다.
__filename, __dirname 키워드로 현재 파일명과 현재 파일의 경로를 확인할 수 있다.

// filename.js
console.log(__filename);
console.log(__dirname);
$ node filename.js
D:\바탕화면\study\node.js\filename.js
D:\바탕화면\study\node.js

ES모듈에서는 __filename__dirname을 사용할 수 없어서 import.meta.url로 경로를 가져올 수 있다.

// filename.mjs
console.log(import.meta.url);
console.log("__filename은 에러");
console.log(__filename);
$ node filename.mjs
file:///D:/%EB%B0%94%ED%83%95%ED%99%94%EB%A9%B4/study/node.js/filename.mjs        
__filename은 에러
file:///D:/%EB%B0%94%ED%83%95%ED%99%94%EB%A9%B4/study/node.js/filename.mjs:3      
console.log(__filename);
            ^

ReferenceError: __filename is not defined in ES module scope
(생략)

0개의 댓글