테크·9 min read
pnpm vs npm vs yarn 완전 비교 2026 — 개발자를 위한 패키지 매니저 선택 가이드
pnpm, npm, yarn의 속도·디스크 효율·모노레포 지원을 2026년 기준으로 비교합니다. 프로젝트 규모와 팀 상황에 맞는 패키지 매니저 선택 기준과 pnpm 마이그레이션 방법을 정리했습니다.
패키지 매니저 선택이 개발 경험을 바꾼다
Node.js 생태계의 패키지 매니저는 npm, yarn, pnpm 세 가지가 주류입니다. 2026년 현재 pnpm이 속도와 디스크 효율, 모노레포 지원에서 두각을 나타내며 채택이 빠르게 늘고 있습니다.
한눈에 비교
| 항목 | npm v10 | yarn v1 (classic) | yarn berry (v4) | pnpm v9 |
|---|---|---|---|---|
| 설치 속도 | 기준 | 빠름 | 빠름 (PnP) | 가장 빠름 |
| 디스크 사용 | 높음 | 높음 | 낮음 (PnP) | 가장 낮음 |
| node_modules | 플랫 호이스팅 | 플랫 호이스팅 | 없음 (PnP) | 엄격한 심볼릭링크 |
| 모노레포 | workspaces 지원 | workspaces 지원 | workspaces 지원 | 최고 수준 |
| 호환성 | 최고 | 높음 | 중간 (PnP 이슈) | 높음 |
| 팬텀 의존성 방지 | X | X | O (PnP) | O |
| 학습 난이도 | 쉬움 | 쉬움 | 어려움 | 쉬움 |
pnpm이 빠른 이유: 콘텐츠 주소 저장소
npm/yarn의 방식
프로젝트 A: node_modules/react@18.3.0 (복사본 1)
프로젝트 B: node_modules/react@18.3.0 (복사본 2)
프로젝트 C: node_modules/react@18.3.0 (복사본 3)
→ 디스크에 react 3개 복사본 존재
pnpm의 방식
~/.pnpm-store/v3/files/... (전역 저장소, 1개만 존재)
↑ 하드링크
프로젝트 A: node_modules/.pnpm/react@18.3.0/
↑ 심볼릭링크
프로젝트 A: node_modules/react → .pnpm/react@18.3.0/node_modules/react
→ 실제 파일은 한 번만 저장, 참조만 공유
실제 측정 결과 (react + 100개 의존성 프로젝트 기준)
| 작업 | npm | yarn v1 | pnpm |
|---|---|---|---|
| 최초 설치 (캐시 없음) | 45s | 38s | 28s |
| 재설치 (캐시 있음) | 22s | 12s | 5s |
| CI 환경 (캐시 없음) | 40s | 35s | 18s |
| 디스크 사용량 | 250MB | 240MB | 80MB |
설치 및 기본 명령어
pnpm 설치
# npm을 통한 설치
npm install -g pnpm
# 또는 공식 설치 스크립트 (curl)
curl -fsSL https://get.pnpm.io/install.sh | sh -
# 또는 Homebrew (macOS)
brew install pnpm
# 버전 확인
pnpm --version
명령어 비교
| 작업 | npm | yarn v1 | pnpm |
|---|---|---|---|
| 설치 | npm install | yarn | pnpm install |
| 패키지 추가 | npm install react | yarn add react | pnpm add react |
| 개발 의존성 추가 | npm install -D eslint | yarn add -D eslint | pnpm add -D eslint |
| 패키지 삭제 | npm uninstall react | yarn remove react | pnpm remove react |
| 스크립트 실행 | npm run build | yarn build | pnpm build |
| 전역 설치 | npm install -g pkg | yarn global add pkg | pnpm add -g pkg |
| lock 파일 기반 설치 | npm ci | yarn install --frozen-lockfile | pnpm install --frozen-lockfile |
| 패키지 목록 | npm list | yarn list | pnpm list |
| 업데이트 | npm update | yarn upgrade | pnpm update |
npm에서 pnpm으로 마이그레이션
1단계: pnpm 설치 및 import
# pnpm 설치
npm install -g pnpm
# package-lock.json → pnpm-lock.yaml 변환
pnpm import
# 또는 처음부터 재생성 (더 깨끗한 방법)
rm package-lock.json
pnpm install
2단계: package.json 설정
{
"name": "my-app",
"engines": {
"node": ">=20",
"pnpm": ">=9"
},
"packageManager": "pnpm@9.12.0"
}
3단계: .npmrc 설정
# .npmrc
# 엄격한 node_modules 구조 유지 (권장)
strict-peer-dependencies=false
auto-install-peers=true
# 필요 시 호이스팅 활성화 (호환성 문제 해결용)
# shamefully-hoist=true
4단계: .gitignore 업데이트
# .gitignore에 추가
node_modules
# pnpm-lock.yaml은 커밋 대상 (package-lock.json과 동일)
5단계: CI/CD 업데이트
# GitHub Actions
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm build
pnpm 모노레포 설정
pnpm-workspace.yaml
# pnpm-workspace.yaml (루트에 위치)
packages:
- 'apps/*'
- 'packages/*'
- '!**/__tests__/**'
모노레포 디렉토리 구조
my-monorepo/
├── pnpm-workspace.yaml
├── package.json
├── apps/
│ ├── web/ # Next.js 앱
│ │ └── package.json
│ └── api/ # Express 서버
│ └── package.json
└── packages/
├── ui/ # 공유 컴포넌트
│ └── package.json
└── utils/ # 공유 유틸리티
└── package.json
루트 package.json
{
"name": "my-monorepo",
"private": true,
"scripts": {
"dev": "pnpm -r --parallel run dev",
"build": "pnpm -r run build",
"lint": "pnpm -r run lint",
"test": "pnpm -r run test"
},
"devDependencies": {
"turbo": "^2.0.0"
}
}
모노레포 주요 명령어
# 전체 설치
pnpm install
# 전체 빌드
pnpm -r run build
# 특정 패키지만 실행
pnpm --filter @myapp/web dev
pnpm --filter @myapp/api start
# 특정 패키지에 의존성 추가
pnpm --filter @myapp/web add react
pnpm --filter @myapp/web add -D tailwindcss
# 워크스페이스 내 패키지 참조
# apps/web/package.json:
# "dependencies": { "@myapp/ui": "workspace:*" }
Turborepo 연동 (빌드 캐싱)
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {
"outputs": []
}
}
}
# Turborepo로 병렬 빌드 + 캐싱
pnpm turbo build
# 변경된 패키지만 빌드 (CI 최적화)
pnpm turbo build --filter='[HEAD^1]'
언제 무엇을 선택할까
pnpm 선택 기준
- 모노레포를 운영하거나 계획 중
- CI 속도와 디스크 효율이 중요
- 팬텀 의존성 문제를 방지하고 싶음
- 새 프로젝트 시작 시 (기본 선택)
npm 유지 기준
- 팀이 npm에 익숙하고 전환 비용이 부담
- 레거시 패키지 호환성이 최우선
- 단순한 소규모 프로젝트
yarn v1 유지 기준
- 기존 yarn v1 프로젝트이고 굳이 전환할 이유 없음
- (yarn v1은 더 이상 활발히 개발되지 않음)
yarn berry 선택 기준
- Zero-Installs 전략이 꼭 필요 (CI 환경 최적화)
- 팀 전체가 PnP 설정에 익숙함
- 기존 yarn berry 프로젝트 유지
자주 겪는 문제와 해결
"Cannot find module" 오류 (팬텀 의존성)
# 문제: package.json에 선언 안 된 패키지에 접근 시
Error: Cannot find module 'lodash'
# 해결: 직접 의존성으로 추가
pnpm add lodash
# 임시 해결 (권장 X): .npmrc에 추가
shamefully-hoist=true
peer dependencies 경고
# 자동 설치 활성화
echo "auto-install-peers=true" >> .npmrc
# 엄격한 peer deps 검사 비활성화 (필요 시)
echo "strict-peer-dependencies=false" >> .npmrc
pnpm store 정리
# 사용하지 않는 패키지 정리
pnpm store prune
# 전체 store 위치 확인
pnpm store path
정리
| npm | pnpm | |
|---|---|---|
| 초보자 진입 장벽 | 낮음 | 낮음 |
| 속도 | 보통 | 빠름 |
| 디스크 효율 | 낮음 | 높음 |
| 모노레포 | 기본 지원 | 강력한 지원 |
| 생태계 호환성 | 최고 | 높음 |
| 2026년 추천도 | ★★★☆☆ | ★★★★★ |
새 프로젝트라면 pnpm, 기존 npm 프로젝트라면 pnpm import 한 줄로 마이그레이션하는 것을 권장합니다.
관련 글: GitHub Actions CI/CD 설정법 · Next.js + MDX 블로그 만들기 · Vercel 무료 배포 완전 가이드