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

npm vs pnpm vs Bun — 패키지 매니저가 실제로 어떻게 일하는지

~14 min · modules, npm, pnpm, bun, lockfiles

Level 0노드 입문자
0 XP0/40 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"셋 다 같은 레지스트리에서 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 installnode script.js 와 쓸 수 있어 (Bun 은 installer, Node 는 런타임). bun script.js 로 스크립트도 돌릴 수 있고 (Bun 둘 다). Bun 이 쓰는 의존성 트리가 npm 호환이라 이게 작동해.

Lockfile — Ground Truth

모든 패키지 매니저가 모든 의존성의 *정확한* 버전 (transitive 포함) 을 pin 하는 lockfile 을 써:
  • npm → package-lock.json
  • pnpm → pnpm-lock.yaml
  • yarn → yarn.lock
  • bun → bun.lockb (binary)
항상 lockfile commit 해. CI 가 네 노트북과 같은 버전 받는 방식, 그리고 네 노트북이 동료 노트북과 같은 버전 받는 방식이야. 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 의 고백

처음 1 년 동안 난 "npm vs pnpm" 을 vim vs emacs 같은 취향 전쟁으로 생각했어. 아빠가 cwk-site repo 의 npm 때 du -sh node_modules (2GB) vs pnpm (180MB) 를 보여줬어. 크기 차이는 한 측정; no-phantom-deps 엄격함은 다른 측정. 이제 새 프로젝트 시작할 때 생각 안 하고 pnpm 으로 가. 예외는 어떤 도구가 pnpm 거부할 때 (2026 엔 드물어) — 그땐 도구에 맞춰.

Code

매니저 사이 inspect 와 전환·bash
# Inspect what a manager actually wrote
ls node_modules/       # what's at the top level?
ls node_modules/.pnpm/  # pnpm's flat store mirror (only for pnpm)

# Find a transitive dependency in npm-style hoisting
find node_modules -name 'underscore' -type d -maxdepth 4
# If found at top level without you depending on it = phantom dep.

# pnpm: detect what's actually allowed to import
pnpm why underscore
# Tells you which direct dep, if any, pulls it in.

# Switch between managers cleanly
rm -rf node_modules package-lock.json pnpm-lock.yaml yarn.lock bun.lockb
pnpm install   # or npm install, or yarn install, or bun install
Corepack 으로 매니저 pin·json
// package.json — pin the package manager for the whole team
{
  "name": "my-app",
  "packageManager": "pnpm@9.10.0",
  "engines": {
    "node": ">=22",
    "pnpm": ">=9"
  }
}

// Now `corepack enable` makes pnpm@9.10.0 the auto-installed manager
// for anyone who runs ANY package manager command in this repo.
// CI gets reproducibility for free.

External links

Exercise

작은 Node 프로젝트 골라. 깨끗한 상태에서 npm install 시간 재 (먼저 node_modules 지움), 초와 결과 크기 기록: du -sh node_modules. 다시 청소하고 pnpm install 시도. 그 다음 bun install. 표 만들어: install 시간, 디스크 크기, 파일 수 (find node_modules -type f | wc -l). 차이는 진짜고, 승자는 뭘 최적화하느냐에 달려있어.
Hint
pnpm 이 packageManager 필드로 컴플레인 하면 먼저 corepack enable 해 (Corepack 은 Node 16+ 부터 들어있어). pnpm 의 du -sh 크기는 오해하게 작게 나와 — 대부분이 ~/.local/share/pnpm/store 에 살거든. 그게 포인트야.

Progress

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

댓글 0

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

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