Logo

Node.js에서 ES 모듈(import/export) 사용하기

예전에는 Node.js에서는 import, export와 같은 ES Modules(ESM) 문법을 사용하려면 Babel과 같은 트랜스파일러(transpiler)러를 사용해서 코드 변환을 해줬어야 했는데요. Node.js 버전 13.2부터는 CommonJS 뿐만 아니라 ES Modules에 대한 정식 지원이 시작됨에 따라 굳이 트랜스파일링을 하지 않더라도 Node.js에서 ES 모듈을 사용할 수 있게 되었습니다. 🎉

Node.js의 ES 모듈 지원

ES 모듈(ES Modules)은 ES6/ES2015의 일부로 도입되어 현재 자바스크립트의 표준 모듈 시스템으로 서서히 자리 잡아가고 있습니다. Node.js의 ES 모듈 지원은 백엔드(backend) 개발만 하시던 분들에게는 대수롭지 않을 수도 있지만, 자바스크립트 풀스택(fullstack) 개발자들에게는 상당히 의미있는 변화일 것입니다. 왜냐하면 프론트엔드(frontend) 쪽에서는 빌드 도구의 발달로 이미 상당히 예전부터 ES 모듈 시스템이 주류로 사용되었기 때문입니다. 따라서 개발하시는 자바스크립트 애플리케이션이 브라우저든지 Node.js든지 어느 플랫폼에서 돌아가든지 상관없이 하나의 모듈 시스템을 사용할 수 있는 길이 열린 것입니다.

CommonJS와 ES 모듈에 대한 좀 더 자세한 설명은 아래 관련 포스팅를 참고바랍니다.

기존 CommonJS 사용

먼저 기존의 CommonJS 방식을 이용해서 간단한 예제 모듈을 작성해보겠습니다. 아래 time 모듈은 moment 패키지를 불러와서 현재 시간을 문자열로 리턴하는 now() 함수를 내보내고 있습니다.

  • time.js
const moment = require("moment");

exports.now = function () {
  return moment().format();
};

같은 방식으로 테스트 모듈도 작성해보겠습니다. 아래 time.test 모듈은 time 모듈을 불러와서 now 함수의 호출 결과를 출력하고 있습니다.

  • time.test.js
const { now } = require("./time");

console.log("Now:", now());

test.test.js 파일을 실행해보면 다음과 같이 예상대로 작동을 합니다.

$ node src/time.test.js
Now: 2020-05-23T17:43:28-04:00

방법 1: 파일 단위로 ES 모듈 적용

Node.js에서 ES 모듈을 사용하는 첫번째 방법은 파일의 확장자를 js 대신에 mjs를 사용하는 것입니다. 프로젝트에서 부분적으로 ES 모듈을 사용할 때 가장 쉽고 빠르게 적용할 수 있는 방법입니다.

위에서 작성한 time.jstime.test.js 파일의 확장자를 mjs 바꾸고, ES 모듈의 importexport 키워드를 사용하도록 코드를 수정합니다.

  • time.mjs
import moment from "moment";

export function now() {
  return moment().format();
}
  • time.test.mjs
import { now } from "./time";

console.log("Now:", now());

time.test.mjs 파일을 실행을 해보면 Node.js가 time 모듈을 찾지 못하는 현상을 보게 되실 겁니다.

$ node src/time.test.mjs
internal/modules/run_main.js:54
    internalBinding('errors').triggerUncaughtException(
                              ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/dale/temp/es-modules/src/time' imported from /Users/dale/temp/es-modules/src/time.test.mjs
    at finalizeResolution (internal/modules/esm/resolve.js:284:11)
    at moduleResolve (internal/modules/esm/resolve.js:662:10)
    at Loader.defaultResolve [as _resolve] (internal/modules/esm/resolve.js:752:11)
    at Loader.resolve (internal/modules/esm/loader.js:97:40)
    at Loader.getModuleJob (internal/modules/esm/loader.js:242:28)
    at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:50:40)
    at link (internal/modules/esm/module_job.js:49:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

이 부분이 Node.js에서 ES 모듈울 처음 사용할 때 가장 많이 실수를 하게되는 부분인데요. Node.js에서 import 키워드로 프로젝트 내부 모듈을 불러올 때는 반드시 확장자까지 포함해서 경로를 명시를 해줘야 합니다. 이는 브라우저에서 import가 작동하는 방식과 맞추기 위해서 의도적으로 설계된 부분이라고 합니다.

확장자를 포함해서 경로를 명시해주면 정상적으로 작동합니다.

  • time.test.mjs
import { now } from "./time.mjs";

console.log("Now:", now());
$ node src/time.test.mjs
Now: 2020-05-23T18:10:20-04:00

방법 2: 프로젝트 단위로 ES 모듈 적용

Node.js에서 ES 모듈을 사용하는 두번째 방법은 package.json 파일 설정을 통해 전체 파일에 적용하는 것입니다. 모든 파일의 확장자를 일일이 바꾸지 않고, 프로젝트 전체에 ES 모듈을 적용하고 싶을 때 적합한 방법입니다.

먼저 프로젝트의 package.json 파일을 열고, 최상위에 type 항목을 module로 설정합니다.

  • package.json
{
  // 생략
  "type": "module"
  // 생략
}

다시 파일의 확장자를 mjs에서 js로 바꾸고, time.test 모듈 안의 확장자도 마찬가지로 업데이트 해줍니다.

import { now } from "./time.js";

console.log("Now:", now());

다시 time.test.js 파일을 실행을 해보면 위와 동일하게 실행이 되는 것을 볼 수 있습니다.

$ src/time.test.js
Now: 2020-05-23T18:21:20-04:00

마치면서

이번 포스팅에서는 Node.js 프로젝트에서 모듈 시스템으로 ES Modules을 사용하는 2가지 방법에 대해서 알아보았습니다. 참고로 Node.js 버전 13.2 미만에서도 버전 12 이상에서는 Node.js를 실행할 때 --experimental-module 옵션을 넘기면 동일한 방법으로 ES 모듈을 사용할 수 있으니 참고바라겠습니다.