TL;DR

  • pnpm 패키지 매니저의 핵심 개념을 빠르게 파악할 수 있다.
  • 배경과 이유를 통해 왜 필요한지 맥락을 이해할 수 있다.
  • 특징과 상세 내용을 통해 실무 적용 포인트를 확인할 수 있다.

1. 개념

pnpm 패키지 매니저의 핵심 정의와 문제 공간을 간단히 정리한다.

2. 배경

이 주제가 등장한 기술적·조직적 배경과 기존 접근의 한계를 설명한다.

3. 이유

왜 지금 이 방식을 채택해야 하는지, 기대 효과와 트레이드오프를 함께 정리한다.

4. 특징

핵심 동작 방식, 장단점, 적용 시 주의점을 빠르게 훑을 수 있도록 요약한다.

5. 상세 내용

pnpm 패키지 매니저

작성일: 2026-02-09 수정일: 2026-02-26 카테고리: Build / Package Manager 포함 내용: pnpm, npm, yarn, Content-Addressable Store, Phantom Dependencies, Monorepo, workspace, 보안, 역사, Corepack, Catalogs


1. pnpm이란?

개념

pnpm = Performant npm
      └── "빠르고 디스크 효율적인" 패키지 매니저
      └── npm/yarn의 문제점을 해결하기 위해 등장

패키지 매니저 기초 설명

┌─────────────────────────────────────────────────────────────────┐
│              패키지 매니저가 뭔가요? (완전 기초)                  │
│                                                                   │
│  비유: 레고 블록 조달 시스템                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  프로그래밍 = 레고로 집을 짓는 것                        │    │
│  │  패키지 = 다른 사람이 만든 레고 블록 세트                │    │
│  │  패키지 매니저 = 레고 블록 배달/관리 서비스              │    │
│  │                                                          │    │
│  │  직접 만들기: 벽돌 하나하나 직접 제작 → 시간 낭비        │    │
│  │  패키지 사용: "벽돌 10개 주세요" → 자동 배달             │    │
│  │                                                          │    │
│  │  npm/yarn/pnpm 모두 이 "배달 서비스" 역할                │    │
│  │  차이점: 배달 방식과 보관 방식이 다름                     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  핵심 개념들:                                                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  package.json = 주문서 (어떤 블록이 필요한지 목록)       │    │
│  │  node_modules = 배달된 블록 보관 창고                    │    │
│  │  lock 파일 = 정확한 블록 버전 기록표                     │    │
│  │  registry = 블록을 파는 중앙 시장 (npmjs.com)            │    │
│  │  의존성(dependency) = 내가 쓰는 블록이 필요로 하는 블록  │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

2. 등장 배경

npm/yarn의 문제점

┌─────────────────────────────────────────────────────────────────┐
│                                                                   │
│  기존 방식 (npm/yarn):                                           │
│                                                                   │
│  프로젝트 A          프로젝트 B          프로젝트 C              │
│  └── node_modules/   └── node_modules/   └── node_modules/      │
│      └── lodash@4        └── lodash@4        └── lodash@4       │
│      └── react@18        └── react@18        └── react@18       │
│      └── axios@1         └── axios@1         └── axios@1        │
│      └── ...             └── ...             └── ...            │
│                                                                   │
│  문제점:                                                         │
│  ├── 같은 패키지가 프로젝트마다 중복 설치                        │
│  ├── 디스크 공간 낭비 (프로젝트당 수백 MB ~ 수 GB)              │
│  ├── 설치 시간 증가                                              │
│  └── 네트워크 대역폭 낭비                                        │
│                                                                   │
│  실제 사례:                                                      │
│  "10개 프로젝트 × 500MB = 5GB 디스크 사용"                      │
│  "같은 lodash가 10번 복사됨"                                     │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

Flat node_modules의 문제

┌─────────────────────────────────────────────────────────────────┐
│                                                                   │
│  npm v3+, yarn의 "Flat" 구조:                                   │
│                                                                   │
│  의도: 중첩 깊이 줄이기 (Windows 경로 길이 제한 등)             │
│                                                                   │
│  node_modules/                                                   │
│  ├── A/               ← 직접 의존성                              │
│  ├── B/               ← A의 의존성인데 최상위로 끌어올림        │
│  ├── C/               ← B의 의존성인데 최상위로 끌어올림        │
│  └── ...                                                         │
│                                                                   │
│  부작용: Phantom Dependencies 발생                               │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

3. 패키지 매니저의 역사 - 누가 왜 만들었나?

3.1 npm의 탄생 (2010)

┌─────────────────────────────────────────────────────────────────┐
│                   npm의 탄생 이야기                               │
│                                                                   │
│  시대: 2009-2010년                                               │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Ryan Dahl이 Node.js를 만듦 (2009년 5월)                │    │
│  │  → 서버에서 JavaScript를 실행할 수 있게 됨!              │    │
│  │                                                          │    │
│  │  문제: 코드를 공유할 방법이 없음                         │    │
│  │  ├── ZIP 파일 다운로드                                   │    │
│  │  ├── GitHub에서 직접 복사                                 │    │
│  │  └── 버전 관리? 그런 건 없었음                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Isaac Z. Schlueter (Yahoo 개발자):                              │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  "이거 너무 불편하다. 패키지 매니저를 만들자!"           │    │
│  │                                                          │    │
│  │  2010년 1월 12일: npm 첫 공개 릴리스                     │    │
│  │  2011년: Node.js에 기본 포함됨                            │    │
│  │  2014년: npm Inc. 설립 (상업화)                           │    │
│  │  2020년: GitHub(Microsoft)에 인수됨                       │    │
│  │                                                          │    │
│  │  현재: 130만+ 패키지, 월 750억+ 다운로드                 │    │
│  │  → 세계 최대 소프트웨어 레지스트리                        │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  npm이 만든 3가지 핵심 개념:                                     │
│  ├── 1. package.json: 프로젝트의 "주문서"                       │
│  ├── 2. 중앙 레지스트리: npmjs.com이라는 "시장"                  │
│  └── 3. Semantic Versioning: 1.2.3 (주.부.패치) 버전 체계       │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

3.2 left-pad 사건 (2016년 3월) - npm 생태계의 충격

┌─────────────────────────────────────────────────────────────────┐
│           left-pad 사건 - 11줄의 코드가 인터넷을 멈추다          │
│                                                                   │
│  2016년 3월 22일, 세계적인 사건 발생:                             │
│                                                                   │
│  배경:                                                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Azer Koculu라는 개발자가 'kik'이라는 npm 패키지를 소유  │    │
│  │  → Kik 메신저 회사가 상표권 주장                          │    │
│  │  → npm이 Kik 회사 편을 들어 패키지명을 빼앗음            │    │
│  │  → 분노한 Koculu가 자신의 273개 패키지를 전부 삭제!      │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  문제:                                                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  삭제된 패키지 중 'left-pad' = 문자열 왼쪽 채우기 함수   │    │
│  │  고작 11줄짜리 코드                                       │    │
│  │                                                          │    │
│  │  그런데... 이 패키지를 의존하는 프로젝트:                 │    │
│  │  ├── Babel (JavaScript 컴파일러)                          │    │
│  │  ├── React 개발 도구                                      │    │
│  │  ├── Webpack                                              │    │
│  │  └── Facebook, Netflix, Spotify 등 수천 개 프로젝트       │    │
│  │                                                          │    │
│  │  결과: 전 세계적으로 빌드가 깨짐                          │    │
│  │  "11줄의 코드가 인터넷을 멈췄다"                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  이 사건이 드러낸 npm의 구조적 문제:                              │
│  ├── 1. 레지스트리 연결 없으면 빌드 불가                         │
│  ├── 2. Lock 파일이 없어서 설치 재현 불가                        │
│  ├── 3. 누구나 패키지를 삭제할 수 있었음                         │
│  └── 4. 의존성 체인의 취약성이 명백해짐                          │
│                                                                   │
│  → 이 사건이 yarn과 pnpm 탄생의 직접적 촉매가 됨                │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

3.3 yarn의 등장 (2016년 10월) - Facebook의 해결책

┌─────────────────────────────────────────────────────────────────┐
│                 yarn - Facebook이 만든 이유                       │
│                                                                   │
│  Facebook 내부 상황 (2016년):                                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  JavaScript 코드베이스가 거대해지면서:                    │    │
│  │                                                          │    │
│  │  문제 1: 비결정적 설치                                    │    │
│  │  → npm install을 2번 실행하면 다른 결과가 나옴            │    │
│  │  → "내 컴퓨터에선 되는데?" 문제가 빈번                    │    │
│  │                                                          │    │
│  │  문제 2: 엄청난 용량                                      │    │
│  │  → React Native의 68개 의존성 → 121,358개 파일 생성       │    │
│  │  → Babel 업데이트 한 번에 80만 줄 diff                    │    │
│  │                                                          │    │
│  │  문제 3: 느린 순차 설치                                   │    │
│  │  → npm은 패키지를 하나씩 순서대로 다운로드                │    │
│  │  → 대규모 프로젝트에서 수 분씩 걸림                       │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  핵심 인물:                                                      │
│  ├── Sebastian McKenzie: Babel 창시자, yarn 프로젝트 시작        │
│  ├── Christoph Nakazawa: Facebook 런던, JS 도구 팀장             │
│  └── 협력: Google, Exponent(Expo), Tilde(Ember.js)               │
│                                                                   │
│  yarn이 가져온 혁신:                                             │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  1. yarn.lock: 정확한 버전 고정 → 재현 가능한 설치       │    │
│  │  2. 병렬 다운로드: 동시에 여러 패키지 다운로드            │    │
│  │  3. 오프라인 캐시: 한 번 다운로드하면 오프라인 설치 가능  │    │
│  │  4. 체크섬 검증: 패키지 변조 방지                         │    │
│  │                                                          │    │
│  │  결과: 설치 시간 수 분 → 수 초로 단축                     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  하지만 yarn도 해결하지 못한 것:                                 │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Flat node_modules 구조 유지 (npm과 동일)                │    │
│  │  Phantom Dependencies 여전히 발생                         │    │
│  │  디스크 공간 낭비 (프로젝트별 복사)                       │    │
│  │                                                          │    │
│  │  → 이것이 pnpm이 탄생한 이유!                            │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

3.4 pnpm의 탄생 (2017) - 근본적 해결책

┌─────────────────────────────────────────────────────────────────┐
│              pnpm - Zoltan Kochan의 근본적 해결책                │
│                                                                   │
│  Zoltan Kochan의 불만:                                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  "yarn이 나왔을 때 기대했지만 실망했다."                  │    │
│  │  "빠르긴 한데, 근본적인 문제를 안 고쳤다."               │    │
│  │  "Flat node_modules가 진짜 문제인데 왜 아무도 안 고치지?" │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  선구자: Alexander Gugel의 'ied' (2015)                          │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  심볼릭 링크 기반 의존성 관리 실험                        │    │
│  │  → 이 아이디어에서 pnpm이 영감을 받음                    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  타임라인:                                                       │
│  ├── 2016년 1월: Rico Sta. Cruz가 초기 커밋                      │
│  ├── 2017년 6월: pnpm v1 정식 릴리스 (Zoltan Kochan 주도)       │
│  ├── 2020년: Yarn Classic 유지보수 모드 진입 → pnpm 급성장       │
│  ├── 2021년: Vue.js, Vite가 pnpm으로 전환                        │
│  ├── 2025년 1월: pnpm v10 "보안 기본값" 마일스톤                 │
│  └── 2026년 현재: 다운로드 수 매년 2배 성장 중                   │
│                                                                   │
│  pnpm의 핵심 철학:                                               │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  "문제의 근본을 고치자"                                   │    │
│  │                                                          │    │
│  │  npm/yarn: Flat 구조의 부작용을 땜질로 해결               │    │
│  │  pnpm: Flat 구조 자체를 버리고 새로운 구조 도입           │    │
│  │                                                          │    │
│  │  = Content-Addressable Store + 심볼릭 링크                │    │
│  │  = 디스크 절약 + Phantom Dependencies 원천 차단           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

3.5 Yarn Berry (v2+)와 PnP - 그리고 실패

┌─────────────────────────────────────────────────────────────────┐
│           Yarn Berry의 급진적 실험과 생태계의 거부                │
│                                                                   │
│  Yarn Classic이 유지보수 모드에 들어가며 (2020):                 │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Mael Nison(arcanis)이 Yarn을 완전히 새로 작성           │    │
│  │  → Yarn Berry (v2+)                                       │    │
│  │  → 핵심 기능: Plug'n'Play (PnP)                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  PnP의 아이디어:                                                 │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  "node_modules 폴더 자체를 없애버리자!"                   │    │
│  │                                                          │    │
│  │  방법:                                                    │    │
│  │  1. 패키지를 ZIP 파일로 .yarn/cache/에 저장               │    │
│  │  2. .pnp.cjs 파일로 모든 경로를 매핑                      │    │
│  │  3. Node.js의 fs 모듈을 덮어씌워서 ZIP에서 직접 읽기     │    │
│  │                                                          │    │
│  │  장점: 설치 거의 즉시, Zero-Install 가능                  │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  왜 실패했나:                                                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  - Node.js 표준이 아님 (런타임 패치 필요)                │    │
│  │  - 수많은 라이브러리/도구와 호환 안 됨                    │    │
│  │  - Webpack, Jest, IDE 플러그인 등이 깨짐                  │    │
│  │  - Facebook 자체도 Yarn Classic에 머무름                   │    │
│  │  - 2024년 기준 주요 오픈소스 프로젝트 중 PnP 사용 = 0    │    │
│  │                                                          │    │
│  │  교훈: 너무 급진적인 변화는 생태계가 따라오지 않는다      │    │
│  │                                                          │    │
│  │  반면 pnpm은:                                             │    │
│  │  + node_modules 유지 (호환성 보장)                        │    │
│  │  + Node.js 표준 그대로 동작                               │    │
│  │  + 기존 도구와 거의 100% 호환                             │    │
│  │  → "현실적인 혁신"이 승리함                               │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

4. pnpm의 해결책

Content-Addressable Store

┌─────────────────────────────────────────────────────────────────┐
│                                                                   │
│  pnpm 방식:                                                      │
│                                                                   │
│  비유: 도시 중앙 도서관 시스템                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  npm/yarn = 각 집(프로젝트)마다 책(패키지)을 구매         │    │
│  │  pnpm = 중앙 도서관(store)에 책 1권, 각 집은 대출카드    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  ~/.pnpm-store/              ← 글로벌 저장소 (딱 1곳)            │
│  └── v3/                                                         │
│      └── files/                                                  │
│          └── 00/                                                 │
│          └── 01/                                                 │
│          └── ...             ← 해시 기반 파일 저장               │
│                                                                   │
│  프로젝트 A                  프로젝트 B                          │
│  └── node_modules/           └── node_modules/                   │
│      └── .pnpm/              └── .pnpm/                          │
│      └── lodash → 하드링크   └── lodash → 하드링크              │
│      └── react → 하드링크    └── react → 하드링크               │
│                                                                   │
│  핵심:                                                           │
│  ├── 패키지 파일은 store에 1번만 저장                            │
│  ├── 프로젝트에서는 하드 링크로 참조                             │
│  ├── 디스크 사용량 대폭 감소 (70-90%)                           │
│  └── 설치 속도 향상 (이미 store에 있으면 링크만)                │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

심볼릭 링크 vs 하드 링크

┌─────────────────────────────────────────────────────────────────┐
│                                                                   │
│  심볼릭 링크 (Symbolic Link):                                    │
│  ├── "바로가기" 같은 개념                                        │
│  ├── 원본 파일을 가리키는 포인터                                 │
│  └── 원본 삭제되면 깨짐                                          │
│                                                                   │
│  하드 링크 (Hard Link):                                          │
│  ├── 같은 파일 데이터를 가리키는 또 다른 이름                    │
│  ├── 원본과 동등한 존재                                          │
│  ├── 원본 삭제해도 데이터 유지                                   │
│  └── 디스크 공간 추가 사용 없음                                  │
│                                                                   │
│  비유:                                                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  심볼릭 링크 = "3층 302호에 가세요" (안내문)             │    │
│  │  → 302호가 사라지면 안내문은 무의미                       │    │
│  │                                                          │    │
│  │  하드 링크 = 같은 방에 문이 2개 있는 것                   │    │
│  │  → 문 하나를 막아도 다른 문으로 들어갈 수 있음            │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  pnpm:                                                           │
│  ├── 패키지 폴더 → .pnpm/ : 심볼릭 링크                         │
│  └── 파일 → store : 하드 링크                                   │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

5. Phantom Dependencies 차단

문제 설명

┌─────────────────────────────────────────────────────────────────┐
│                                                                   │
│  Phantom Dependency (유령 의존성):                               │
│                                                                   │
│  비유: 친구의 친구 물건을 마음대로 쓰는 것                      │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  내가 A한테 빌린 도구 → 정당한 사용                       │    │
│  │  A가 갖고 있는 B의 도구를 내가 마음대로 사용 → 위험!     │    │
│  │  → A가 B와 절교하면 나도 그 도구를 못 씀                 │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  package.json:                                                   │
│    dependencies: { "express": "4.18" }                           │
│                                                                   │
│  express가 내부적으로 "accepts" 패키지 사용                      │
│                                                                   │
│  npm/yarn (flat node_modules):                                   │
│  node_modules/                                                   │
│  ├── express/                                                    │
│  └── accepts/  ← express의 의존성이지만 최상위에 노출됨         │
│                                                                   │
│  내 코드에서:                                                    │
│  import accepts from 'accepts'  // 동작함! (하지만 위험)        │
│                                                                   │
│  문제:                                                           │
│  ├── package.json에 없는 패키지를 사용 가능                      │
│  ├── express가 accepts 제거하면 내 코드도 깨짐                   │
│  └── 의존성 그래프가 불명확해짐                                  │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

pnpm의 엄격한 구조

┌─────────────────────────────────────────────────────────────────┐
│                                                                   │
│  pnpm node_modules 구조:                                         │
│                                                                   │
│  node_modules/                                                   │
│  ├── .pnpm/                      ← 실제 패키지들 (격리됨)        │
│  │   ├── express@4.18.0/                                        │
│  │   │   └── node_modules/                                      │
│  │   │       ├── express/        ← 실제 파일                    │
│  │   │       └── accepts/        ← express만 접근 가능          │
│  │   └── accepts@1.3.8/                                         │
│  │       └── node_modules/                                      │
│  │           └── accepts/                                       │
│  └── express/ → .pnpm/express@4.18.0/.../express                │
│                                                                   │
│  내 코드에서:                                                    │
│  import express from 'express'   // 정상                         │
│  import accepts from 'accepts'   // 에러! (올바른 동작)          │
│                                                                   │
│  → 명시적으로 선언한 의존성만 접근 가능                          │
│  → 더 안전하고 예측 가능한 의존성 관리                           │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

6. npm vs yarn vs pnpm 비교

기능 비교

항목 npm yarn (classic) pnpm
출시 2010 2016 2017
저장 방식 프로젝트별 복사 프로젝트별 복사 글로벌 store + 링크
디스크 사용 높음 높음 매우 낮음
설치 속도 보통 빠름 가장 빠름
Lock 파일 package-lock.json yarn.lock pnpm-lock.yaml
node_modules flat flat (PnP 옵션) 엄격한 중첩
Phantom Deps 허용 허용 차단
Monorepo workspaces workspaces 최적화된 workspace

성능 벤치마크 (일반적인 경우)

┌─────────────────────────────────────────────────────────────────┐
│                                                                   │
│  설치 시간 (cold cache):                                         │
│  npm:  ████████████████████  100%                                │
│  yarn: ████████████████      80%                                 │
│  pnpm: ████████████          60%                                 │
│                                                                   │
│  설치 시간 (warm cache - 이미 store에 있음):                     │
│  npm:  ████████████████████  100%                                │
│  yarn: ████████████████      80%                                 │
│  pnpm: ████                  20%  ← 링크만 생성                  │
│                                                                   │
│  디스크 사용량:                                                  │
│  npm:  ████████████████████  100%                                │
│  yarn: ████████████████████  100%                                │
│  pnpm: ██████                30%  ← 공유 store                   │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

7. 대형 프로젝트들의 pnpm 전환 사례

┌─────────────────────────────────────────────────────────────────┐
│            왜 대형 오픈소스가 pnpm을 선택했나?                    │
│                                                                   │
│  Vue.js (2021년 10월):                                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  전환 이유: esbuild v0.13이 플랫폼별 바이너리를          │    │
│  │  optionalDependencies로 변경                              │    │
│  │                                                          │    │
│  │  Yarn: 모든 플랫폼 바이너리 다운로드 (~102MB)            │    │
│  │  pnpm: 현재 플랫폼 바이너리만 다운로드 (~14.5MB)         │    │
│  │  → 7배 차이!                                              │    │
│  │                                                          │    │
│  │  커뮤니티 투표: 90%가 pnpm 추천                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Vite (Vue와 동시기):                                            │
│  ├── 공식 저장소가 pnpm 모노레포                                 │
│  ├── 현대 프론트엔드 빌드 도구의 표준                            │
│  └── React, Svelte, Lit 생태계 전체에 영향                       │
│                                                                   │
│  Turborepo + Vercel:                                             │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  pnpm workspace + Turborepo 조합이                        │    │
│  │  모노레포의 사실상 표준(de facto standard)이 됨           │    │
│  │                                                          │    │
│  │  pnpm: 의존성 관리 + 워크스페이스 연결                    │    │
│  │  Turborepo: 태스크 오케스트레이션 + 캐싱                  │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  기타 주요 채택:                                                 │
│  ├── Prisma (DB ORM)                                             │
│  ├── SvelteKit (공식 템플릿에 포함)                              │
│  ├── Nuxt 3 (모노레포에 pnpm 권장)                              │
│  └── VueUse, Slidev 등 Anthony Fu의 프로젝트들                  │
│                                                                   │
│  2024년 사용률 (State of Frontend 설문, 6000명+):                │
│  ├── npm: 56.6% (Node.js 기본 포함이라 높음)                     │
│  ├── Yarn Classic: 21.5%                                         │
│  └── pnpm: 19.9% (만족도 93%로 최고)                            │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

8. pnpm의 보안 장점 - 공급망 공격 방어

8.1 event-stream 사건 (2018)

┌─────────────────────────────────────────────────────────────────┐
│           event-stream 사건과 pnpm의 보안 대응                   │
│                                                                   │
│  2018년 11월, 주당 200만 다운로드의 event-stream 패키지:         │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  공격 과정:                                               │    │
│  │  1. 공격자가 원래 관리자에게 관리 권한을 양도받음          │    │
│  │  2. flatmap-stream이라는 악성 의존성을 추가                │    │
│  │  3. postinstall 스크립트로 악성 코드 실행                  │    │
│  │  4. 비트코인 지갑(Copay) 정보 탈취                        │    │
│  │                                                          │    │
│  │  pnpm v10이었다면?                                        │    │
│  │  → postinstall 스크립트 기본 차단!                        │    │
│  │  → 이 공격은 원천 봉쇄되었을 것                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  비유:                                                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  npm/yarn = 택배 받자마자 자동으로 상자를 열어줌          │    │
│  │  → 상자 안에 폭탄이 있어도 열어버림                       │    │
│  │                                                          │    │
│  │  pnpm v10 = 택배는 받되, 열기 전에 허가를 받아야 함      │    │
│  │  → "이 택배 열어도 되나요?" 물어봄                        │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

8.2 pnpm v10의 보안 기본값 (2025)

┌─────────────────────────────────────────────────────────────────┐
│              pnpm v10 - "보안을 기본값으로"                       │
│                                                                   │
│  1. Lifecycle Scripts 기본 차단                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  npm/yarn: postinstall 스크립트 자동 실행 (위험!)         │    │
│  │  pnpm v10: 기본적으로 차단, 명시적 허용 필요              │    │
│  │                                                          │    │
│  │  // package.json                                          │    │
│  │  "pnpm": {                                                │    │
│  │    "allowedBuilds": ["sharp", "esbuild"]                  │    │
│  │  }                                                        │    │
│  │  → 신뢰하는 패키지만 빌드 스크립트 실행 허용              │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  2. 엄격한 node_modules = 공급망 공격 방어                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Phantom Dependency 차단의 보안적 의미:                   │    │
│  │                                                          │    │
│  │  npm/yarn: 악성 패키지가 다른 패키지의 파일에 접근 가능   │    │
│  │  pnpm: 각 패키지는 자기 의존성에만 접근 가능              │    │
│  │  → 악성 코드의 피해 범위가 제한됨                         │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  3. 릴리스 나이 필터링 (minimumReleaseAge)                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  // .npmrc                                                │    │
│  │  minimum-release-age=1440  // 24시간 (분 단위)            │    │
│  │                                                          │    │
│  │  → 새로 릴리스된 패키지를 24시간 대기 후 설치             │    │
│  │  → 제로데이 공격을 발견할 시간 확보                        │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  4. 이상한 출처 차단 (blockExoticSubdeps)                        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  // .npmrc                                                │    │
│  │  block-exotic-subdeps=true                                │    │
│  │                                                          │    │
│  │  → 의존성이 git URL이나 tarball에서 오는 것을 차단        │    │
│  │  → npm 레지스트리를 통한 패키지만 허용                    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

9. 주요 명령어

기본 명령어

# pnpm 설치
npm install -g pnpm
# 또는
corepack enable  # Node.js 16.13+ 내장

# 버전 확인
pnpm --version

# 패키지 설치
pnpm install                  # 모든 의존성 설치
pnpm add lodash               # 프로덕션 의존성 추가
pnpm add -D typescript        # 개발 의존성 추가
pnpm add -g tsx               # 전역 설치

# 패키지 제거
pnpm remove lodash

# 스크립트 실행
pnpm run build                # npm run build와 동일
pnpm build                    # run 생략 가능
pnpm test
pnpm start

Store 관리

# store 경로 확인
pnpm store path
# → ~/.local/share/pnpm/store/v3

# store 상태 확인
pnpm store status

# 사용하지 않는 패키지 정리
pnpm store prune

유용한 옵션

# 프로젝트의 모든 의존성 업데이트
pnpm update

# 특정 패키지만 업데이트
pnpm update lodash

# 의존성 트리 확인
pnpm list
pnpm list --depth=2

# 왜 이 패키지가 설치됐는지 확인
pnpm why lodash

10. Monorepo 지원

pnpm-workspace.yaml

# pnpm-workspace.yaml
packages:
  - 'packages/*'
  - 'apps/*'
  - '!**/test/**'  # test 폴더 제외

Monorepo 구조

┌─────────────────────────────────────────────────────────────────┐
│                                                                   │
│  my-monorepo/                                                    │
│  ├── package.json           ← 루트 package.json                  │
│  ├── pnpm-workspace.yaml    ← workspace 설정                    │
│  ├── pnpm-lock.yaml         ← 단일 lock 파일                    │
│  ├── node_modules/          ← 공유 의존성                        │
│  ├── apps/                                                       │
│  │   ├── web/               ← Next.js 앱                        │
│  │   │   └── package.json                                       │
│  │   └── api/               ← Express 서버                      │
│  │       └── package.json                                       │
│  └── packages/                                                   │
│      ├── ui/                ← 공유 컴포넌트                      │
│      │   └── package.json                                       │
│      └── utils/             ← 공유 유틸리티                      │
│          └── package.json                                       │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

Workspace 명령어

# 특정 패키지에서 명령 실행
pnpm --filter web build
pnpm --filter @my/utils test

# 모든 패키지에서 명령 실행
pnpm -r run build           # recursive
pnpm -r run test

# 의존성 있는 패키지들만 빌드
pnpm --filter web... build  # web과 web이 의존하는 모든 패키지

# 워크스페이스 패키지를 의존성으로 추가
pnpm add @my/utils --filter web --workspace

11. pnpm 최신 기능 (2024-2025)

11.1 Corepack - Node.js 공식 인정

┌─────────────────────────────────────────────────────────────────┐
│                Corepack으로 pnpm 관리하기                         │
│                                                                   │
│  Corepack이란?                                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Node.js 16.9+에 내장된 패키지 매니저 버전 관리 도구      │    │
│  │  → pnpm이 Node.js에 "공식 인정"받은 것                    │    │
│  │                                                          │    │
│  │  비유: 운영체제에 기본 설치된 앱 스토어                    │    │
│  │  npm = 기본 앱, pnpm = 앱 스토어에서 받을 수 있는 앱     │    │
│  │                                                          │    │
│  │  // package.json                                          │    │
│  │  {                                                        │    │
│  │    "packageManager": "pnpm@9.15.0"                        │    │
│  │  }                                                        │    │
│  │                                                          │    │
│  │  corepack enable    // 활성화                             │    │
│  │  pnpm install       // 자동으로 9.15.0 버전 사용          │    │
│  │                                                          │    │
│  │  팀원 모두 같은 pnpm 버전 보장!                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

11.2 Catalogs (pnpm 9.5+) - 모노레포 의존성 통합

┌─────────────────────────────────────────────────────────────────┐
│              Catalogs - 버전을 한 곳에서 관리                     │
│                                                                   │
│  문제: 모노레포에서 10개 패키지가 React를 사용                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  기존: 각 package.json에 버전이 흩어져 있음               │    │
│  │  app-a/package.json: "react": "^18.2.0"                   │    │
│  │  app-b/package.json: "react": "^18.3.0"  // 다른 버전!   │    │
│  │  lib-c/package.json: "react": "^18.2.0"                   │    │
│  │  → 버전 드리프트, 머지 충돌, 업그레이드 번거로움          │    │
│  │                                                          │    │
│  │  Catalogs: 버전을 한 곳에서 선언                          │    │
│  │  // pnpm-workspace.yaml                                   │    │
│  │  catalog:                                                 │    │
│  │    react: ^18.3.0                                         │    │
│  │    typescript: ^5.5.0                                     │    │
│  │                                                          │    │
│  │  // 각 package.json                                       │    │
│  │  "dependencies": { "react": "catalog:" }                  │    │
│  │  → 모든 패키지가 같은 버전 사용 보장!                     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  비유:                                                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  기존 = 각 부서가 따로 사무용품 주문 (규격이 다 다름)    │    │
│  │  Catalogs = 본사에서 공용 규격을 정해놓고 일괄 주문       │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

11.3 pnpm patch - 의존성 직접 수정

┌─────────────────────────────────────────────────────────────────┐
│              pnpm patch - 라이브러리 버그 직접 고치기             │
│                                                                   │
│  상황: lodash에 버그가 있는데 PR이 머지되려면 한참 걸림          │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  pnpm patch lodash          // lodash를 임시 폴더에 추출  │    │
│  │  # → 임시 폴더에서 버그 수정                              │    │
│  │  pnpm patch-commit /tmp/xxx // .patch 파일 생성 및 등록   │    │
│  │                                                          │    │
│  │  // package.json에 자동 기록                              │    │
│  │  "pnpm": {                                                │    │
│  │    "patchedDependencies": {                               │    │
│  │      "lodash": "patches/lodash.patch"                     │    │
│  │    }                                                      │    │
│  │  }                                                        │    │
│  │  → 매번 install할 때 자동으로 패치 적용                   │    │
│  │  → 포크 없이 의존성 버그 수정 가능!                       │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  비유:                                                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  기존 = 차에 결함이 있으면 리콜 올 때까지 기다림          │    │
│  │  pnpm patch = 직접 수리하고 매뉴얼에 기록해둠             │    │
│  │  → 다음에 차를 빌려도 자동으로 같은 수리 적용             │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

11.4 pnpm deploy - 프로덕션 배포 최적화

┌─────────────────────────────────────────────────────────────────┐
│            pnpm deploy - Docker 이미지 최적화                    │
│                                                                   │
│  모노레포에서 하나의 앱만 배포하고 싶을 때:                      │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  pnpm deploy --filter=my-app /output                      │    │
│  │                                                          │    │
│  │  → my-app에 필요한 의존성만 추출                          │    │
│  │  → Docker 이미지 크기 최소화                              │    │
│  │  → 불필요한 devDependencies 제외                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  비유:                                                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  기존 = 이사할 때 집 전체 짐을 다 가져감                  │    │
│  │  pnpm deploy = 새 집에 필요한 것만 골라서 가져감          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

12. 마이그레이션

npm/yarn에서 pnpm으로

# 1. 기존 node_modules 삭제
rm -rf node_modules
rm package-lock.json  # 또는 yarn.lock

# 2. pnpm으로 설치
pnpm install

# 3. pnpm-lock.yaml 생성됨 (커밋 필요)
git add pnpm-lock.yaml
git commit -m "chore: migrate to pnpm"

주의사항

┌─────────────────────────────────────────────────────────────────┐
│                                                                   │
│  마이그레이션 시 주의:                                           │
│                                                                   │
│  1. Phantom Dependencies 에러                                    │
│     → package.json에 누락된 의존성 추가 필요                     │
│     → pnpm add <missing-package>                                │
│                                                                   │
│  2. 일부 패키지 호환성                                           │
│     → shamefully-hoist 옵션으로 flat 구조 허용 가능             │
│     → .npmrc에 shamefully-hoist=true                            │
│     → (권장하지 않음, 임시 해결책)                               │
│                                                                   │
│  3. CI/CD 수정 필요                                              │
│     → npm install → pnpm install                                │
│     → npm run → pnpm run                                        │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

13. 설정 (.npmrc)

주요 설정

# .npmrc

# store 경로 변경 (기본: ~/.local/share/pnpm/store)
store-dir=~/.pnpm-store

# 엄격 모드 (기본값, phantom deps 차단)
strict-peer-dependencies=true

# 호환성 모드 (npm처럼 flat 구조, 권장 안 함)
shamefully-hoist=true

# Node.js 버전 관리
use-node-version=18.17.0

# 레지스트리 설정
registry=https://registry.npmjs.org/

# [pnpm v10] 보안 설정
minimum-release-age=1440
block-exotic-subdeps=true

14. 언제 무엇을 쓰면 좋은가? - 실전 선택 가이드

┌─────────────────────────────────────────────────────────────────┐
│                   실전 패키지 매니저 선택 가이드                  │
│                                                                   │
│  pnpm을 쓰면 좋은 경우:                                         │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  + 모노레포를 운영할 때 (최적의 선택)                     │    │
│  │  + 디스크 공간이 제한적일 때 (CI/CD 러너, 노트북)        │    │
│  │  + 보안이 중요한 프로젝트 (금융, 의료)                    │    │
│  │  + 여러 프로젝트를 동시에 개발할 때                       │    │
│  │  + CI/CD 빌드 시간을 줄이고 싶을 때                       │    │
│  │  + 새 프로젝트를 시작할 때 (처음부터 pnpm 추천)          │    │
│  │  + 의존성을 엄격하게 관리하고 싶을 때                     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  npm을 쓰면 좋은 경우:                                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  + 팀원들이 패키지 매니저에 익숙하지 않을 때              │    │
│  │  + 특별한 요구사항 없이 간단한 프로젝트                   │    │
│  │  + Node.js 설치만으로 바로 시작하고 싶을 때               │    │
│  │  + 기존 npm 프로젝트를 유지보수할 때                      │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  yarn을 쓰면 좋은 경우:                                         │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  + 이미 yarn을 쓰고 있는 프로젝트                         │    │
│  │  + 팀이 yarn에 익숙한 경우                                │    │
│  │  ! 신규 프로젝트라면 pnpm 고려 권장                       │    │
│  │  ! Yarn Classic은 유지보수 모드 (신기능 없음)             │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  판단 플로우차트:                                                │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Q: 모노레포인가?                                        │    │
│  │  ├── YES → pnpm (최적의 선택)                             │    │
│  │  └── NO                                                   │    │
│  │      Q: 보안이 중요한가?                                  │    │
│  │      ├── YES → pnpm (Lifecycle 차단, 엄격한 격리)        │    │
│  │      └── NO                                               │    │
│  │          Q: 디스크/속도 최적화가 필요한가?                │    │
│  │          ├── YES → pnpm                                   │    │
│  │          └── NO                                           │    │
│  │              Q: 팀 학습 비용을 최소화하고 싶은가?         │    │
│  │              ├── YES → npm (가장 간단)                    │    │
│  │              └── NO → pnpm (장기적으로 이점)              │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

15. 정리

┌─────────────────────────────────────────────────────────────────┐
│                                                                   │
│  pnpm = 빠르고 디스크 효율적인 패키지 매니저                     │
│                                                                   │
│  핵심 특징:                                                      │
│  ├── Content-Addressable Store: 패키지 1번만 저장               │
│  ├── 하드 링크: 디스크 공간 절약 (70-90%)                       │
│  ├── 엄격한 node_modules: Phantom Dependencies 차단             │
│  ├── Monorepo 최적화: 효율적인 workspace 관리                   │
│  ├── 보안 기본값: Lifecycle 차단, 릴리스 나이 필터링            │
│  └── 최신 기능: Catalogs, patch, deploy, Corepack               │
│                                                                   │
│  역사적 맥락:                                                    │
│  ├── npm (2010): 최초의 Node.js 패키지 매니저                   │
│  ├── left-pad 사건 (2016): npm 생태계 취약성 드러남             │
│  ├── yarn (2016): Facebook이 만든 빠른 대안                     │
│  ├── pnpm (2017): 근본적 구조 혁신                               │
│  └── Yarn Berry PnP (2020): 급진적 실험, 생태계 거부            │
│                                                                   │
│  보안:                                                           │
│  ├── event-stream 같은 공급망 공격 방어                          │
│  ├── postinstall 스크립트 기본 차단 (v10)                       │
│  ├── minimumReleaseAge로 제로데이 방어                           │
│  └── 엄격한 격리로 악성 코드 피해 범위 제한                     │
│                                                                   │
│  선택 기준:                                                      │
│  ├── 모노레포/보안/디스크/속도 → pnpm                           │
│  ├── 간단한 프로젝트/학습 비용 최소화 → npm                     │
│  └── 기존 yarn 프로젝트 유지보수 → yarn                         │
│                                                                   │
│  주요 기업 사용:                                                 │
│  Vue.js, Vite, Nuxt, Turborepo, Vercel, Prisma,                 │
│  SvelteKit 등                                                    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

관련 키워드

pnpm, npm, yarn, 패키지 매니저, node_modules, Content-Addressable Store, 하드 링크, 심볼릭 링크, Phantom Dependencies, 유령 의존성, Monorepo, workspace, pnpm-workspace.yaml, pnpm-lock.yaml, .npmrc, left-pad, Isaac Z. Schlueter, Zoltan Kochan, Sebastian McKenzie, Yarn Berry, PnP, Plug'n'Play, Corepack, pnpm patch, Catalogs, pnpm deploy, Supply Chain Attack, 공급망 공격, event-stream, Lifecycle Scripts, minimumReleaseAge, Turborepo, Vue.js