pnpm
pnpm
pnpm 은 Node.js 생태계의 패키지 매니저 가운데 하나. 이 글은 pnpm 의 출자 · 동작 방식 · 다른 매니저와의 차이를 사실 기준으로 정리합니다.
1. pnpm 에 대한 이야기
"performant npm" 의 약자로, 2017 년 Zoltan Kochan 이 시작한 오픈소스 프로젝트. 저작권 표기는 "2015–현재 contributors of pnpm" — 초기 실험은 2015 년부터 GitHub 저장소 (pnpm/pnpm) 에서 진행됐고 1.0 을 거쳐 현재 9.x · 10.x 메이저로 이어집니다. 라이선스는 MIT.
해결하려 한 문제는 둘:
- 같은 패키지를 여러 프로젝트에 중복 설치해 디스크가 빠르게 차오르는 npm 의 동작.
- 평탄화 (hoisting) 된
node_modules가 선언하지 않은 의존성을 임포트해도 동작하게 만드는 "phantom dependency" 문제.
2. 어떻게 동작하는가
pnpm 의 핵심은 content-addressable store. 패키지 파일은 ~/.pnpm-store (또는 OS 기본 경로) 에 한 번만 저장되고, 각 프로젝트의 node_modules 에는 그 파일을 가리키는 하드링크. 같은 버전의 라이브러리를 여러 프로젝트가 의존해도 디스크에는 한 본만.
node_modules 구조는 두 단계:
node_modules/.pnpm/<pkg>@<ver>/node_modules/<pkg>— 실제 패키지 (스토어로의 하드링크).node_modules/<직접의존성>— 위 경로로의 심볼릭 링크.
직접 선언한 의존성만 최상위에 노출되므로 package.json 에 적지 않은 모듈을 코드에서 require 할 수 없음. 공식 문서가 "non-flat node_modules" 라 부릅니다.
3. 워크스페이스
루트의 pnpm-workspace.yaml 에 패키지 경로 패턴:
packages:
- "apps/*"
- "packages/*"
내부 의존성은 "workspace:*" 같은 프로토콜로 적어 외부 npm 레지스트리 대신 워크스페이스 내부를 가리킴.
pnpm-lock.yaml 은 모든 의존성의 정확한 버전과 해시를 기록 — 커밋 대상.
4. 다른 길
| 도구 | 첫 릴리스 | 특징 |
|---|---|---|
| npm | 2010 | Node.js 공식 동봉. 평탄화된 node_modules. 가장 큰 호환 폭. |
| Yarn Classic (1.x) | 2016 | Facebook 발. 락파일 · 워크스페이스를 npm 보다 먼저 도입. 현재 유지보수 모드. |
| Yarn Berry (2+) | 2020 | PnP (Plug'n'Play) 로 node_modules 자체를 없애는 모드. 도구 호환성 문제로 채택이 갈림. |
| pnpm | 2017 | 스토어 + 하드링크 / 심링크. 디스크 절약 · 엄격한 의존성. |
| Bun | 2023 (1.0) | Zig 기반 런타임 + 패키지 매니저. bun install 의 속도가 강점. |
각 도구가 어울리는 환경은 다름. CI 환경이 Node 표준 도구만 가정한다면 npm, 디스크와 모노레포 격리가 중요한 환경에서는 pnpm, 신규 그린필드에 한정해 속도를 우선한다면 Bun.
5. 자주 쓰는 모양
# 설치
# Windows: PowerShell
iwr https://get.pnpm.io/install.ps1 -useb | iex
# macOS · Linux: bash
curl -fsSL https://get.pnpm.io/install.sh | sh -
# 프로젝트에서
pnpm install # 의존성 설치
pnpm add zod # 런타임 의존성 추가
pnpm add -D vitest # 개발 의존성
pnpm remove zod
pnpm update
pnpm run build # 또는 그냥 pnpm build
워크스페이스 루트에서 특정 패키지 명령 실행:
pnpm --filter web dev
pnpm -r build # 모든 워크스페이스 패키지에 build 실행
6. engines · packageManager
package.json 에 Node 와 pnpm 버전을 적어 놓는 관습:
{
"engines": {
"node": ">=20.10",
"pnpm": ">=9"
},
"packageManager": "pnpm@9.12.3"
}
packageManager 필드는 Corepack (Node 16.10+ 동봉) 이 읽어 자동으로 해당 매니저 버전을 준비.
7. 자주 걸리는 자리
phantom dependency — 일부 도구가 평탄화된 node_modules 를 가정해 phantom dependency 를 쓰면 pnpm 환경에서 깨짐. 보통 누락된 의존성을 package.json 에 명시하면 해결. 임시 회피로 .npmrc 의 shamefully-hoist=true 가 있지만 의도가 흐려짐.
Windows 하드링크 — 사용자 프로필이 다른 드라이브에 있으면 하드링크가 같은 볼륨 안에서만 동작하는 제약 때문에 스토어 위치를 프로젝트와 같은 드라이브로 옮겨야 할 때. 환경변수 PNPM_HOME 또는 pnpm config set store-dir.
락파일 혼재 — npm 과 pnpm 의 락파일이 한 저장소에 섞이면 의존성 해석이 흔들림. CI 와 로컬에서 같은 매니저를 쓰도록 packageManager 필드와 Corepack 을 함께.
pnpm dlx vs npx — 캐시 경로가 달라 한쪽이 캐시한 도구를 다른 쪽이 다시 받는 경우.
하고픈 말
pnpm 의 content-addressable store + 비호이스팅 구조가 디스크 절약과 phantom dependency 차단 두 가치를 동시에 줍니다. 모노레포에서는 이 차이가 더 크게 드러나는 자리. packageManager 필드 + Corepack 으로 팀 · CI 가 같은 버전을 쓰도록 고정.
Next
- monorepo-light
- python-uv
pnpm 공식 문서 · pnpm motivation · pnpm GitHub · npm Documentation · Yarn Documentation · Node.js Corepack · pnpm 블로그 를 참고합니다.