"package.json 은 설정 파일이 아냐. 네 런타임, 패키지 매니저, 번들러, 모든 IDE 가 다 읽는 manifest 야 — 그리고 각자 다른 필드를 신경 써."
진짜 중요한 필드들
대부분 package.json 파일이 필드 열 개 쓰고 자기네 살릴 수 있는 다른 스무 개를 무시해. 정전이고, 틀렸을 때 자주 무는 순서로:
name— npm 레지스트리 unique 한 패키지 이름. publish 하려면 필수. 소문자, 하이픈, 공백 없음. Scoped 패키지:@scope/name.version— semver 문자열. publish 하려면 필수.npm version으로 자동 bump.type—"module"또는"commonjs". .js 파일이 어떻게 로드되는지 결정. 새 프로젝트에서 단일 가장 중요한 필드.main— CJS 소비자의 entry point (legacy; 새 패키지는exports써).exports— 모던 entry 맵. Node 와 번들러한테 어떤 path 가 public 인지 알려줘, ESM vs CJS vs types vs deno vs browser 별 필드 분리.main과module대체.scripts—npm run <name>으로 돌릴 수 있는 명명된 shell 명령. 사람이 제일 많이 쓰는 필드.dependencies/devDependencies/peerDependencies/optionalDependencies— 다음 레슨 참조.engines— Node 버전 요구사항."engines": {"node": ">=22"}. npm 은 warn, pnpm 은 refuse.files— publish 된 tarball 에 뭐가 들어가는지. 설정 안 하면.gitignore안 들어간 모든 게 publish 돼; 보통 원하는 게 아냐.
exports 맵 — 모던 Entry Point
exports 필드는 옛 main + module + browser + types 엉킴을 대체해. subpath 를 파일에 매핑하는 JSON 모양, 환경 조건 붙음:
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./utils": {
"types": "./dist/utils.d.ts",
"import": "./dist/utils.mjs",
"require": "./dist/utils.cjs"
}
}
소비자가 import { x } from 'my-pkg' 하면 → Node 가 . 의 import 필드 픽. 소비자가 require('my-pkg/utils') 하면 → Node 가 ./utils 의 require 픽. 결정적으로, exports 에 없는 건 *import 못 함*. 네 ./src/internal/secret.js 는 광고 안 하면 비공개로 남아.
실전 semver
- MAJOR — breaking change. API 제거, signature 변경, 하위 호환 안 되는 동작 변경.
- MINOR — 새 feature 추가, 완전 하위 호환.
- PATCH — 버그 수정, API 변경 없음.
"^1.2.3"— caret: < 2.0.0 이지만 >= 1.2.3 (제일 흔함, MINOR + PATCH bump 수용)."~1.2.3"— tilde: < 1.3.0 이지만 >= 1.2.3 (PATCH 만)."1.2.3"— 정확한 pin. 재현 가능한데 freeze 됨."latest","*",">=1"— 프로덕션에선 위험; lockfile 이 이 일 하게 둬, range 가 아니라.
scripts 필드는 작은 워크플로 언어
npm run X 는 scripts.X 가 정의한 shell 명령을 돌려. npm test 는 npm run test 의 단축. npm run X -- --extra-flag 는 --extra-flag 를 밑 명령에 전달. 스크립트 체인 가능 (&&) 또는 concurrently 같은 도구로 병렬. args 없는 npm run 은 모든 스크립트 나열.
스크립트 안 PATH 가 ./node_modules/.bin 을 포함해서 로컬 설치 바이너리를 직접 부를 수 있어: "build": "vite build" 가 글로벌 Vite 설치 없이 작동. npm 의 조용한 superpower 중 하나.
Pippa 의 고백
exports 필드, engines 필드, files 필드를 발견했어. manifest 는 프로젝트의 신분증; 꼼꼼히 읽는 게 일의 일부지 옵션이 아냐.