핵심 정리
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
// ESM에서 __filename, __dirname 대체 패턴
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// 현재 파일 기준으로 다른 파일 경로 구성
const configPath = join(__dirname, "config", "default.json");
// Node 20.11+ 에서는 간결하게
const dir = import.meta.dirname;
const file = import.meta.filename;파일 기준점
먼저 머릿속에 잡아 둘 기본형은 아래입니다.
- 현재 모듈 위치:
import.meta.url - URL -> path:
fileURLToPath(import.meta.url) - 현재 디렉터리 계산:
dirname(...) - 최신 단축형:
import.meta.dirname,import.meta.filename - 모듈 해석 결과:
import.meta.resolve(...)
ESM의 기준 위치는 process.cwd()가 아니라 import.meta.url이다
실행 위치가 어디든 현재 모듈 옆 파일을 읽고 싶다면 process.cwd()로 가면 안 됩니다. 이 카드의 핵심은 "현재 파일 기준 경로"를 잡는 법입니다.
import.meta.url은 파일 경로가 아니라 URL이다
file:///... 형태라서 path.join()이나 fs.readFile()에 바로 넣으면 틀어질 수 있습니다. 파일 시스템 API에 넣기 전에는 fileURLToPath()로 변환하는 습관이 필요합니다.
Node 20.11+면 더 짧아질 수 있다
최신 Node에서는 import.meta.dirname, import.meta.filename가 있어 예전 변환 패턴을 짧게 줄일 수 있습니다. 이 값들은 20.11+에서 도입됐고 22.16+/24.0+부터는 더 이상 experimental이 아니므로, 팀 런타임 버전을 같이 보는 편이 정확합니다.
모듈 해석이 필요하면 import.meta.resolve()를 쓴다
현재 파일 옆 경로 계산과, 패키지 해석 결과를 얻는 일은 다릅니다. 후자는 import.meta.resolve()가 더 정확합니다.
현재 파일 기준 경로와 패키지 해석을 섞지 않는다
./data.json처럼 모듈 옆 파일을 찾는 일과, "some-package/subpath"를 해석하는 일은 문제 종류가 다릅니다.
전자는 import.meta.url, 후자는 import.meta.resolve()로 나눠 읽는 편이 덜 헷갈립니다.
언제 URL을 쓰나
체크포인트
- 현재 모듈 위치:
import.meta.url - 파일 경로 변환:
fileURLToPath(import.meta.url) - 디렉터리 계산:
dirname(...) - 최신 Node 단축형:
import.meta.dirname - 패키지 해석 결과:
import.meta.resolve(...) - 현재 파일 기준 경로와 패키지 해석을 분리해서 본다
주의할 점
import.meta.url은 문자열처럼 보여도 파일 경로가 아니다. fileURLToPath()를 빼먹으면 경로 처리에서 생각보다 자주 깨진다.
참고 링크
2 sources