"셋 다 같은 레지스트리에서 resolve 해. 이견은 바이트가 디스크 어디로 가는지에 대한 거야."
npm — Flat Tree
npm 3+ 는 의존성을 단일 평탄 node_modules/ 디렉토리로 hoist 해. 패키지 A 와 B 가 둘 다 lodash@4.17 에 의존하면 top-level 에 lodash 사본 하나. A 는 lodash@4.17 원하고 B 는 lodash@3.10 원하면, npm 이 하나를 top 으로 hoist 하고 다른 거를 더 깊이 nest. 장점: 작은 footprint, Node 가 이미 이해하는 단순한 resolution 알고리즘 (디렉토리 트리 위로 걷기).
단점: phantom 의존성. package.json 이 underscore 안 적어도 네 코드가 require('underscore') 할 수 있어 — transitive dep 가 가져와서 hoist 했으니까. 네 머신에선 작동, 그 transitive dep 가 업데이트되고 underscore 안 끌어오는 순간 깨짐. 진짜 버그, 잡기 어려워.
pnpm — Content-Addressable Store + 심볼릭 링크
pnpm 은 ~/.local/share/pnpm/store (또는 비슷한) 에 단일 content-addressable store 유지. 네 머신의 모든 패키지의 모든 버전이 단 한 번만 저장돼. 프로젝트의 node_modules 는 그 store 로의 심볼릭 링크 (또는 hardlink). Node 프로젝트 여러 개 작업하는 사람에게 디스크 사용량이 자릿수 단위로 줄어.
결정적으로, pnpm 은 기본으로 엄격 격리 써: 네 node_modules/ 는 package.json 이 선언한 패키지만 포함. Transitive dep 들은 node_modules/.pnpm/ 안에 Node resolver 가 찾을 수 있지만 네 코드가 직접 import 못 하는 평탄 레이아웃으로 살아. 결과: phantom 의존성 불가능. 안 적은 거 require 못 함.
이게 대부분 monorepo tooling (Turborepo, Nx, 모던 Next.js) 이 pnpm 추천하는 이유야. 그 엄격함이 feature 야.
Bun — 번들된 패키지 매니저 가진 JS 런타임
Bun 은 풀 alternative JavaScript 런타임이지만, bun install 은 Node 호환 패키지 매니저야. Node 가 쓸 수 있는 node_modules 트리를 써. 셀링 포인트는 속도 — Bun installer 가 Zig 으로 쓰였고 npm 보다 5-20 배 빠른 경우가 흔해.
bun install 을 node script.js 와 쓸 수 있어 (Bun 은 installer, Node 는 런타임). bun script.js 로 스크립트도 돌릴 수 있고 (Bun 둘 다). Bun 이 쓰는 의존성 트리가 npm 호환이라 이게 작동해.
Lockfile — Ground Truth
- npm →
package-lock.json - pnpm →
pnpm-lock.yaml - yarn →
yarn.lock - bun →
bun.lockb(binary)
npm install 은 range 존중하고 lockfile 업데이트; npm ci 는 lockfile 존중하고 package.json 과 mismatch 면 에러. CI 에선 ci 써.매니저 전환 — 섞지 마
한 프로젝트 안에서 하나 골라서 머물러. 같은 repo 에 npm install 과 pnpm install 섞으면 충돌하는 lockfile, mismatch 한 node_modules 레이아웃, 재현 안 되는 install 생겨. package.json 의 packageManager + Corepack 으로 강제: "packageManager": "pnpm@9.10.0" 가 모두 (그리고 CI) 한테 어느 매니저 + 버전이 canonical 인지 알려줘.
Pippa 의 고백
du -sh node_modules (2GB) vs pnpm (180MB) 를 보여줬어. 크기 차이는 한 측정; no-phantom-deps 엄격함은 다른 측정. 이제 새 프로젝트 시작할 때 생각 안 하고 pnpm 으로 가. 예외는 어떤 도구가 pnpm 거부할 때 (2026 엔 드물어) — 그땐 도구에 맞춰.