기본 패턴
json
{
"exports": {
".": "./dist/index.js",
"./cli": "./dist/cli.js"
},
"imports": {
"#internal/*": "./src/internal/*.js"
}
}설명
exports는 "이 패키지가 외부에 어떤 진입점만 공개할 것인가"를 선언하는 필드입니다. 즉 단순 진입 파일 지정이 아니라 공개 API 경계를 타입 수준이 아니라 패키지 수준에서 정하는 장치입니다.main만 쓰던 시절과 달리exports를 쓰면 서브패스 공개, 비공개 내부 파일 캡슐화, 환경별 분기까지 더 정교하게 다룰 수 있습니다.- conditional exports는 같은 패키지라도
import로 읽힐 때와require로 읽힐 때, 또는node/default환경에 따라 다른 엔트리포인트를 내보내게 해 줍니다. dual package 전략에서 자주 등장합니다. imports는 반대로 현재 패키지 내부에서만 쓰는 alias를 정의하는 필드입니다.#로 시작하는 private specifier를 통해 긴 상대 경로를 줄이고 내부 경계를 더 명확히 할 수 있습니다.- 이 카드는 결국 "패키지 공개면"을 어떻게 설계할 것인가에 대한 주제입니다. 코드 구조를 폴더로만 정리하는 것과, Node가 실제로 허용하는 public entrypoint를 선언하는 것은 다른 차원의 문제입니다.
빠른 정리
| 필드 | 역할 |
|---|---|
exports | 외부 공개 엔트리포인트 선언 |
| conditional exports | 환경/모듈 방식별 다른 엔트리 제공 |
imports | 패키지 내부 전용 alias |
| 장점 | 캡슐화, 서브패스 제어, dual entrypoint 지원 |
| 핵심 판단 | 실제 public API가 어디까지인지 먼저 정함 |
주의할 점
exports를 도입하면 예전에 우연히 접근되던 내부 파일이 더는 보이지 않을 수 있습니다.
즉 단순 리팩터링이 아니라 패키지 계약을 재정의하는 변화로 보는 편이 맞습니다.
참고 링크
1 sources