type: module로 바꿨더니 __dirname is not defined가 난 이유
문제 발생
package.json에 "type": "module"을 넣자 파일을 읽던 코드가 전부 죽었습니다.
ReferenceError: __dirname is not defined in ES module scopeconst template = fs.readFileSync(path.join(__dirname, "template.html"), "utf8");require로 JSON을 읽던 코드도 함께 깨졌습니다.
원인 분석
ES 모듈에는 그 변수들이 없습니다. Node.js 문서가 그대로 적습니다 — 이 CommonJS 변수들은 ES 모듈에서 사용할 수 없습니다. __filename과 __dirname의 용례는 import.meta.filename과 import.meta.dirname으로 대체할 수 있습니다.
이유는 설계 차이입니다. CommonJS 모듈은 함수로 감싸져 실행되면서 __dirname 같은 지역 변수를 주입받지만, ES 모듈은 그런 래퍼가 없습니다. 대신 표준이 정한 import.meta가 모듈 자신에 대한 정보를 제공합니다.
JSON 쪽도 규칙이 다릅니다. 문서가 명시합니다 — import 로 JSON을 참조할 때 with { type: 'json' } 문법은 필수입니다.
해결 방안
- 경로는
import.meta.dirname을 씁니다.
import path from "node:path";
const template = fs.readFileSync(path.join(import.meta.dirname, "template.html"), "utf8");- URL 기반으로 읽는 방식이 더 낫습니다. 문서의 예제 형태이고, 경로 결합 실수를 줄여줍니다.
import { readFileSync } from "node:fs";
const buffer = readFileSync(new URL("./data.json", import.meta.url));- JSON import에는 속성을 붙입니다.
import pkg from "./package.json" with { type: "json" };- 꼭
require가 필요하면 만들어 씁니다. 문서가 안내하는 방법입니다 — 필요하다면module.createRequire()로 ES 모듈 안에서require함수를 구성할 수 있습니다.
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);-
반대 방향의 제약도 알아둡니다. 문서 설명대로 CommonJS의
require()는 top-level await를 쓰지 않는 동기 ES 모듈만 불러올 수 있습니다. 라이브러리가 top-level await를 쓰기 시작하면 CJS 소비자가 깨집니다. -
전환은 패키지 단위로 합니다. 한 패키지 안에서 두 방식을 섞으면 확장자(
.mjs/.cjs)와 조건부 export가 얽혀 디버깅이 어려워집니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.