기본 패턴
javascript
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const pkg = require("./legacy.cjs");설명
- Node.js는 ESM과 CommonJS를 모두 지원하지만, 둘은 단순히 문법만 다른 것이 아니라 로딩 규칙과 런타임 메타데이터도 다릅니다. 그래서 섞어 쓰기 시작하면 interop 규칙을 정확히 알아야 합니다.
- ESM 안에서는 기본적으로
require,__filename,__dirname이 없습니다. 이럴 때 CommonJS 모듈을 꼭 불러와야 하면createRequire()가 안전한 다리 역할을 합니다. - CommonJS 쪽에서는 정적
import를 바로 쓸 수 없지만,import()는 비동기적으로 ESM을 불러오는 데 사용할 수 있습니다. 즉 방향에 따라 다리가 다릅니다. - CommonJS 모듈을 ESM에서 가져올 때는 대체로
module.exports가 default export처럼 보이게 되고, named export처럼 쓰는 경우는 기대와 다를 수 있습니다. 이 지점이 실무에서 가장 자주 헷갈립니다. - 좋은 전략은 가능하면 프로젝트 전체 모듈 방식을 일관되게 유지하고, interop는 필요한 경계에서만 최소화하는 것입니다. 섞어 쓸 수 있다는 사실과 섞어 써야 한다는 사실은 다릅니다.
빠른 정리
| 상황 | 자주 쓰는 방법 |
|---|---|
| ESM 안에서 CommonJS 사용 | createRequire(import.meta.url) |
| CommonJS 안에서 ESM 사용 | import() |
| 프로젝트 전반 모듈 통일 | 가장 안전한 선택 |
| ESM에서 CJS 가져오기 | default 성격으로 읽히는 경우가 많음 |
| 핵심 원칙 | interop는 경계에서만 최소화 |
주의할 점
ESM과 CommonJS를 자유롭게 섞을 수 있다고 해서 아무 파일에서나 왕복하는 구조를 만들면 실행 방식과 테스트 환경이 금방 복잡해집니다. 경계 파일을 명확히 두는 편이 훨씬 낫습니다.
참고 링크
2 sources