목표

pnpm이 Global Store, Virtual Store(.pnpm), 개별 앱 간에 디스크 공간을 절약하고 유령 의존성을 차단하는 3단계 계층 원리를 이해하고 구조화하기

기억하기

  • 1단계 (글로벌 스토어): 실제 파일 원본이 유일하게 저장되는 CAS(Content-Addressable Storage) 공간 (~/Library/pnpm/store/v3)
  • 2단계 (가상 스토어): 글로벌 스토어와 하드 링크(Hard Link)로 연결되어 실제 디스크 추가 용량이 0B인 평탄화 공간 (node_modules/.pnpm)
  • 3단계 (개별 앱): 가상 스토어를 가리키는 심볼릭 링크(Symbolic Link, 바로가기)만 배치되어 엄격한 참조 격리를 유지하는 공간 (apps/*/node_modules)

내용

1. 3계층 연결 흐름도

[1] Global Content Store (원천 데이터 1벌 저장)
    ~/Library/pnpm/store/v3
              │
              │ (하드 링크: 디스크 inode 공유, 용량 0B 소모)
              ▼
[2] Project Virtual Store (가상 저장소)
    my-monorepo/node_modules/.pnpm/react@18.2.0/node_modules/react
              │
              │ (심볼릭 링크: 단순 경로 바로가기)
              ▼
[3] Apps / Packages (실제 프로젝트가 바라보는 위치)
    ├── apps/web/node_modules/react
    └── apps/tv/node_modules/react

2. 단계별 동작 방식

  • Global Content Store: 패키지 바이너리가 해시 기반으로 시스템 전체에서 단 한 번만 물리 디스크에 기록됨
  • Virtual Store (.pnpm): 같은 파일 시스템의 동일한 inode를 가리키는 하드 링크로 구성되어 복사 비용 및 용량 낭비가 전혀 없음
  • Apps Node Modules: package.json에 명시된 의존성만 Virtual Store 경로로 심볼릭 링크 연결

3. 동작 검증 예시 코드 (터미널 명령어)

# 1. 하드 링크 확인 (동일한 inode 번호를 가리키는지 확인)
ls -i ~/Library/pnpm/store/v3/files/.../data
ls -i ./node_modules/.pnpm/react@18.2.0/node_modules/react/index.js
# 결과: 두 파일의 inode 번호가 일치하며 디스크 용량을 추가 소모하지 않음

# 2. 심볼릭 링크 확인 (가상 스토어로의 상대 경로 참조 확인)
ls -l apps/web/node_modules/react
# 결과: -> ../../../node_modules/.pnpm/react@18.2.0/node_modules/react

느낀점

  • 단순히 "빠르고 가볍다"는 성능적 장점을 넘어, 파일 시스템 수준의 하드 링크와 심볼릭 링크를 정교하게 결합해 OS 레벨의 자원 효율을 극대화한 설계가 인상적임
  • npm의 호이스팅으로 인한 런타임 버그나 모노레포의 중복 디스크 낭비 문제를 아키텍처 수준에서 우아하게 해결함

결론

  • pnpm은 단일 원본 저장(Global) → 하드 링크 가상화(.pnpm) → 심볼릭 링크 주입(Apps) 파이프라인을 통해 디스크 사용량 최소화와 유령 의존성 완전 차단을 동시에 달성함

참고