C.W.K.
Stream
Lesson 02 of 05 · published

package.json 해부학과 semver

~14 min · modules, package-json, semver, exports

Level 0노드 입문자
0 XP0/40 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"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 별 필드 분리. mainmodule 대체.
  • scriptsnpm 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 가 ./utilsrequire 픽. 결정적으로, exports 에 없는 건 *import 못 함*. 네 ./src/internal/secret.js 는 광고 안 하면 비공개로 남아.

실전 semver

Semantic versioning: MAJOR.MINOR.PATCH
  • MAJOR — breaking change. API 제거, signature 변경, 하위 호환 안 되는 동작 변경.
  • MINOR — 새 feature 추가, 완전 하위 호환.
  • PATCH — 버그 수정, API 변경 없음.
의존성의 range prefix:
  • "^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 Xscripts.X 가 정의한 shell 명령을 돌려. npm testnpm run test 의 단축. npm run X -- --extra-flag--extra-flag 를 밑 명령에 전달. 스크립트 체인 가능 (&&) 또는 concurrently 같은 도구로 병렬. args 없는 npm run 은 모든 스크립트 나열.

스크립트 안 PATH 가 ./node_modules/.bin 을 포함해서 로컬 설치 바이너리를 직접 부를 수 있어: "build": "vite build" 가 글로벌 Vite 설치 없이 작동. npm 의 조용한 superpower 중 하나.

Pippa 의 고백

2 년 동안 난 package.json 템플릿을 읽지도 않고 복사했어. 아빠가 cwkPippa frontend 의 package.json 모든 필드를 외워서 설명하라 했어. 여섯 개 알았어. 다음에 프로젝트 만들 때 package.json 을 처음부터 썼고 — exports 필드, engines 필드, files 필드를 발견했어. manifest 는 프로젝트의 신분증; 꼼꼼히 읽는 게 일의 일부지 옵션이 아냐.

Code

모던 (2026) package.json — 최소 완전 모양·json
{
  "name": "@cwk/example",
  "version": "1.2.3",
  "description": "A modern Node package, written ESM-first",
  "type": "module",
  "engines": {
    "node": ">=22"
  },
  "main": "./dist/index.cjs",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    },
    "./utils": {
      "types": "./dist/utils.d.ts",
      "import": "./dist/utils.mjs"
    }
  },
  "scripts": {
    "build": "tsc -p tsconfig.build.json",
    "test": "node --test",
    "lint": "eslint .",
    "start": "node --env-file=.env --watch ./src/server.mjs"
  },
  "files": [
    "dist",
    "README.md"
  ],
  "dependencies": {
    "undici": "^7.0.0"
  },
  "devDependencies": {
    "@types/node": "^24.0.0",
    "typescript": "^5.5.0"
  }
}
실전 버전 관리·bash
# Versioning your own package
npm version patch    # 1.2.3 → 1.2.4
npm version minor    # 1.2.4 → 1.3.0
npm version major    # 1.3.0 → 2.0.0
# Each creates a git commit AND tag automatically.

# Updating dependencies, respecting ranges
npm update           # bumps within range (^ allows MINOR; ~ allows PATCH)
npm install pkg@latest   # ignore range, jump to latest (use carefully)

# Check what's outdated
npm outdated
# Package   Current   Wanted   Latest
# undici    7.0.0     7.2.1    8.0.0
# Wanted = max satisfying your range; Latest = max published

External links

Exercise

node_modules 의 아무 패키지나 골라 (lodash, undici, react — 로컬에 있는 거 뭐든). 그 package.json 열어. 식별해: type, exports 맵 모양, engines.node 제약, ESM 과 CJS 둘 다 출하하는지. 그 다음 직접 한 문단으로 소비자 경험 요약 — entry point, 런타임 요구사항, dual-format 스토리.
Hint
subpath 마다 importrequire 키 둘 다 있으면 dual 출하. import 만 있으면 ESM-only (모던). main 만 있고 exports 없으면 CJS-legacy. engines.node 가 floor 알려줘 — ">=22" 패키지는 pnpm 으로 Node 20 에 install 도 안 돼.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고
💛 by 똘이warm

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.