npm, Yarn, pnpm 비교: 프론트엔드 실무에서의 선택 기준 (Yarn Berry·PnP)

Frontend

프론트엔드 프로젝트를 운영하다 보면 패키지 매니저는 너무 익숙해서 오히려 가볍게 보는 경우가 많습니다. 하지만 실제로는 이 선택이 설치 속도, 빌드 시간, 디스크 사용량, CI 비용, 모노레포 운영 방식, 신규 인력 온보딩까지 꽤 넓은 범위에 영향을 줍니다.

개인 프로젝트에서는 npm이든 yarn이든 pnpm이든 큰 차이가 없어 보일 수 있습니다. 하지만 팀 규모가 커지고 저장소가 복잡해질수록 질문이 달라집니다.

  • 설치 시간이 왜 이렇게 오래 걸리는가
  • CI 캐시를 무엇 기준으로 잡아야 하는가
  • node_modules가 너무 커서 디스크를 잡아먹지 않는가
  • 모노레포에서 워크스페이스가 깔끔하게 유지되는가
  • 새로 합류한 개발자가 별도 설정 없이 바로 개발할 수 있는가
  • 특정 도구가 node_modules를 당연하게 가정하고 있지는 않은가

이 글에서는 npm, Yarn, pnpm을 단순 명령어 비교보다 운영 관점의 선택 기준에 가깝게 정리해보겠습니다. 특히 Yarn BerryPnP는 별도 섹션으로 조금 더 살펴봅니다.

한눈에 보면

먼저 짧게 정리하면 이렇습니다.

  • npm: 가장 기본적인 선택지
  • pnpm: 설치 효율과 운영 편의 사이의 균형이 좋은 편
  • Yarn Berry(PnP): 규칙을 강하게 가져가려는 팀에 잘 맞지만, 운영 방식 변화도 함께 감수해야 함

조금 더 자세히 보면 아래처럼 볼 수 있습니다.

항목 npm Yarn Classic Yarn Berry pnpm
기본 인상 가장 보편적인 기본값 레거시 레포에서 자주 보이는 선택지 정책 강도가 높은 최신 Yarn 설치/디스크/워크스페이스 균형형
설치/디스크 효율 보통 보통 PnP 사용 시 효율이 높음 효율이 높음
node_modules 구조 전통적 전통적 PnP 또는 node-modules 선택 가능 심링크 기반, 전역 스토어 활용
모노레포 운영 가능하지만 상대적으로 평범 가능 강한 정책화 가능 매우 강함
CI 재현성 npm ci로 안정적 --frozen-lockfile로 관리 가능 --immutable로 강하게 고정 가능 --frozen-lockfile로 안정적
생태계 호환성 가장 높음 높음 PnP일 때 이슈 가능 높음
온보딩 난이도 가장 낮음 낮음 상대적으로 높음 낮음~중간
추천 상황 특별한 이유가 없을 때 신규 선택보다는 레거시 유지 엄격한 의존성 관리, zero-install, 정책 강한 레포 모노레포, 설치 속도, 디스크 효율, 현실적 호환성

빠르게 정리하면, 기본값은 npm, 운영 효율까지 보면 pnpm, 강한 규칙이 필요한 팀이면 Yarn Berry(PnP) 정도로 보면 됩니다.

패키지 매니저를 고를 때 무엇을 봐야 할까?

패키지 매니저 선택은 속도 벤치마크 한 장으로 끝나는 문제가 아닙니다. 실무에서는 보통 아래 질문이 더 중요합니다.

  • 설치 시간이 CI에서 얼마나 반복되는가
  • 의존성 캐시 전략을 어떻게 가져갈 것인가
  • 모노레포에서 패키지 간 경계를 얼마나 엄격하게 관리할 것인가
  • 레거시 도구와 IDE가 현재 구조를 잘 이해하는가
  • 신규 입사자가 별도 학습 없이 바로 개발 환경을 띄울 수 있는가
  • 문제가 났을 때 팀이 얼마나 빨리 원인을 찾을 수 있는가

즉, 패키지 매니저는 설치 명령의 차이가 아니라 리포 운영 전략의 차이로 보는 편이 더 정확합니다.

왜 같은 install인데 체감이 다를까?

겉으로 보면 세 도구 모두 결국 "의존성을 내려받고 프로젝트에서 쓸 수 있게 만든다"는 점은 같습니다. 하지만 내부 동작 방식은 꽤 다릅니다.

비교를 단순화하면 이렇게 볼 수 있습니다.

npm / Yarn Classic
  registry -> tarball download -> node_modules에 실제 파일 배치
 
pnpm
  registry -> content-addressable store 저장 -> 프로젝트에는 하드링크/심링크 구성
 
Yarn Berry (PnP)
  registry -> 캐시 저장 -> node_modules 없이 .pnp.cjs로 의존성 위치 해석

즉, 차이는 크게 세 가지입니다.

  • 패키지를 어디에 저장하는가
  • 프로젝트가 그 패키지를 어떤 방식으로 참조하는가
  • 암묵적 의존성을 허용하는가, 더 엄격하게 막는가

이 내부 구조 차이가 결국 설치 속도, 디스크 사용량, 호환성, 디버깅 난이도까지 이어집니다.

각 패키지 매니저의 기본 성격

npm

npm은 가장 보편적인 기본값입니다. Node.js와 함께 오고, 대부분의 문서와 예제가 npm install을 전제로 설명됩니다.

장점은 비교적 명확합니다.

  • 별도 학습 비용이 낮고
  • 생태계 호환성이 가장 높으며
  • npm ci만으로도 CI 재현성을 꽤 안정적으로 가져갈 수 있습니다

예시:

{
  "name": "my-app",
  "packageManager": "npm@10.8.2"
}
npm ci

다만 규모가 커질수록 디스크 효율과 워크스페이스 운영 면에서는 다른 선택지가 더 잘 맞을 수 있습니다.

Yarn Classic

Yarn Classic은 한때 npm보다 빠른 설치 속도, lockfile 안정성, workspace 지원으로 많이 선택됐습니다. 다만 지금 시점에서는 신규 선택지라기보다, 이미 Yarn Classic을 쓰는 레포를 유지하는 상황에 더 가깝습니다.

즉, 과거에는 이유가 분명했지만 현재 기준으로는:

  • 그냥 기본값이 필요하면 npm
  • 운영 효율이 더 중요하면 pnpm
  • 정책 강한 운영이 필요하면 Yarn Berry

로 갈리는 경우가 많아서, Yarn Classic의 위치는 상대적으로 좁아졌습니다.

Yarn Berry

Yarn Berry는 Classic과는 성격이 꽤 다르다고 보는 편이 자연스럽습니다.

대표 특징은 아래와 같습니다.

  • Plug'n'Play (PnP)
  • zero-install
  • constraints
  • packageExtensions
  • 엄격한 의존성 해석

예시:

{
  "name": "my-app",
  "packageManager": "yarn@4.6.0"
}
# .yarnrc.yml
nodeLinker: pnp
enableGlobalCache: true

여기서 짧게만 정리하면:

  • zero-install: .yarn/cache 같은 캐시 산출물을 저장소에 포함해 CI나 신규 개발 환경에서 설치 단계를 거의 생략하는 운영 방식
  • constraints: workspace와 의존성 선언 규칙을 정책처럼 검사해서 CI에서 어긋난 구조를 막는 기능
  • packageExtensions: 레거시 패키지의 잘못되거나 누락된 의존성 메타데이터를 보정하는 기능

Yarn Berry는 단순히 "조금 더 빠른 Yarn"이라기보다, 리포 전체 규칙을 더 엄격하게 관리하는 패키지 매니저에 가깝습니다.

pnpm

pnpm은 최근 몇 년 사이 프론트엔드 팀에서 많이 선택되는 도구 중 하나입니다.

보통 이유는 아래와 같습니다.

  • 설치 속도와 디스크 효율이 좋고
  • 워크스페이스/모노레포 운영에 강하며
  • node_modules 기반을 유지해 생태계 호환성도 상대적으로 높기 때문입니다

예시:

{
  "name": "my-monorepo",
  "packageManager": "pnpm@10.0.0"
}
# pnpm-workspace.yaml
packages:
  - apps/*
  - packages/*

pnpm은 해석 방식을 크게 바꾸기보다, 운영 효율을 높이면서 기존 생태계와의 마찰은 줄이는 방향에 가깝습니다.

운영 관점에서 중요한 비교 축

1. 설치 속도와 디스크 사용량

이 축은 체감 차이가 큰 편입니다. 특히 CI와 모노레포에서 더 그렇습니다.

  • npm: 기본적으로 안정적이지만 대규모 저장소에서는 디스크 사용량이 커질 수 있음
  • Yarn Classic: npm보다 나았던 시기가 있었지만 지금은 상대적 이점이 줄어듦
  • Yarn Berry(PnP): node_modules 자체를 없앨 수 있어서 디스크 사용량을 크게 줄일 수 있음
  • pnpm: 전역 스토어와 하드링크/심링크 구조 덕분에 디스크 효율이 좋음

pnpm은 패키지를 매번 물리적으로 복제하지 않는다는 점이 특징입니다.

pnpm install --frozen-lockfile

이런 차이는 개인 노트북보다 CI 에이전트, 모노레포, 캐시 아티팩트 용량에서 더 잘 드러납니다.

조금 더 기술적으로 보면:

  • npm/Yarn Classic은 패키지를 풀어놓은 결과가 프로젝트별 node_modules에 상대적으로 많이 중복됩니다
  • pnpm은 전역 스토어에 실제 파일을 모아두고 프로젝트에는 링크를 연결합니다
  • Yarn Berry(PnP)는 아예 node_modules를 만들지 않으므로 파일 복제 비용 자체를 크게 줄일 수 있습니다

예를 들어 같은 버전의 react를 20개 패키지가 쓰는 모노레포라면:

  • 전통적 방식은 패키지 배치와 hoisting 결과에 따라 파일 구조가 커지고
  • pnpm은 하나의 저장소를 참조하도록 묶을 수 있으며
  • PnP는 파일 시스템 트리를 거의 만들지 않고 해석 정보로 대체합니다

이 차이는 "설치가 몇 초 빠르다"보다 저장소가 커졌을 때 비용 곡선이 어떻게 달라지는가를 보는 쪽이 더 중요합니다.

2. lockfile 재현성과 CI 설계

실무에서는 로컬에서 되는 설치보다 CI에서 매번 같은 결과가 나오는가가 더 중요합니다.

기본 명령은 보통 이렇게 정리할 수 있습니다.

# npm
npm ci
 
# Yarn Berry
yarn install --immutable
 
# pnpm
pnpm install --frozen-lockfile

중요한 것은 명령어가 아니라 운영 방식입니다.

  • lockfile만 바뀌면 설치 결과가 달라지는가
  • 패키지 매니저 버전까지 고정되어 있는가
  • 캐시 키를 무엇 기준으로 잡는가

실무에서는 보통 packageManager 필드와 corepack을 함께 써서, 특히 Yarn Berrypnpm의 버전까지 같이 고정해두는 편이 안정적입니다.

corepack enable
{
  "packageManager": "pnpm@10.0.0"
}

이렇게 해두면 "누구는 Yarn 3.7, 누구는 4.1" 같은 미묘한 차이도 줄일 수 있습니다.

여기서 한 단계 더 들어가면, 재현성은 단순히 lockfile만의 문제가 아닙니다.

  • 패키지 매니저 버전
  • Node.js 버전
  • .npmrc, .yarnrc.yml, .pnpmfile.cjs 같은 설정 파일
  • OS 차이와 optional dependency 처리

까지 같이 봐야 합니다.

예를 들어 GitHub Actions에서는 보통 이런 식으로 버전을 같이 고정합니다.

- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: 'pnpm'
 
- run: corepack enable
- run: pnpm install --frozen-lockfile

Yarn Berry라면 이런 식입니다.

- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: 'yarn'
 
- run: corepack enable
- run: yarn install --immutable

실무에서 흔한 문제는 lockfile만 커밋해두고, 실제 설치를 결정하는 나머지 조건은 관리하지 않는 경우입니다. 그러면 "재현 가능한 설치"라고 생각했지만, 실제로는 미묘한 차이가 계속 생깁니다.

3. 빌드 타임과 캐시 전략

패키지 매니저는 빌드 도구가 아니지만, 빌드 타임에 간접적으로 큰 영향을 줍니다.

예를 들면:

  • 설치 시간이 길면 전체 CI 시간이 밀리고
  • 캐시가 비효율적이면 빌드 이전 단계에서 이미 시간을 다 쓰며
  • node_modules 크기가 크면 캐시 복원 시간 자체가 병목이 되기도 합니다

이 지점에서는 pnpmYarn Berry가 상대적으로 강한 편입니다.

  • pnpm: 전역 스토어 캐시 전략을 잘 잡으면 설치 비용을 줄이기 좋음
  • Yarn Berry: zero-install까지 운영하면 설치 단계를 저장소 정책과 캐시 관리 문제로 바꿀 수 있음

하지만 여기에도 대가가 있습니다.

  • pnpm: CI 캐시에 무엇을 넣을지 팀이 이해해야 하고
  • Yarn Berry: 저장소 정책과 에디터/도구 설정까지 팀이 같이 이해해야 합니다

즉, 빠른 설치 자체보다 운영 전략을 팀이 얼마나 일관되게 유지할 수 있는가가 더 중요합니다.

실제로 CI에서 자주 부딪히는 선택은 이겁니다.

  • node_modules 자체를 캐시할 것인가
  • 패키지 매니저의 전역 스토어만 캐시할 것인가
  • lockfile 변경 시 어디까지 캐시를 무효화할 것인가

보통은 아래처럼 생각하는 편이 현실적입니다.

  • npm: node_modules 캐시보다 npm cache 또는 npm ci의 일관성에 더 기대는 편
  • pnpm: 전역 스토어 캐시가 효율적이고, 모노레포에서 특히 이점이 큼
  • Yarn Berry: zero-install이면 캐시 전략 자체를 저장소 운영 정책과 묶어서 볼 수 있음

다만 zero-install은 저장소 용량, 리뷰 복잡도, 캐시 파일 변경량 증가 같은 다른 비용을 가져옵니다. 즉, 설치 단계를 단순하게 만드는 대신 저장소 운영 비용을 더 가져오는 선택일 수 있습니다.

4. node_modules 레이아웃 vs PnP

이 지점이 Yarn Berry의 차이를 가장 잘 보여주는 부분입니다.

node_modules가 무거운 이유를 설명하는 이미지

조금 단순화된 이미지이긴 하지만, node_modules가 왜 자주 문제가 되는지는 잘 보여줍니다. 프론트엔드 저장소가 커질수록 설치 시간, 디스크 사용량, 캐시 용량, IDE 인덱싱 비용이 모두 여기에 영향을 받기 때문입니다.

대부분의 패키지 매니저는 결국 node_modules를 만듭니다. 구조가 조금 다를 뿐입니다.

반면 PnP는 아예 접근 방식을 바꿉니다.

  • 패키지 해석을 .pnp.cjs 같은 매핑으로 처리하고
  • node_modules를 만들지 않거나 최소화하며
  • 잘못된 암묵적 의존성을 더 빨리 드러낼 수 있습니다

예시:

# .yarnrc.yml
nodeLinker: pnp

장점:

  • 디스크 효율이 좋고
  • 의존성 경계가 엄격해지며
  • "우연히 hoisting돼서 돌아가던 코드"를 빨리 잡아낼 수 있습니다

단점:

  • 일부 도구가 여전히 node_modules를 당연하게 가정하고
  • IDE, ESLint 플러그인, 테스트 도구, 네이티브 모듈이 friction을 만들 수 있으며
  • 레거시 패키지가 있을수록 운영 난이도가 올라갑니다

즉, PnP는 기술적으로 장점이 있지만 조직 차원에서 함께 관리해야 할 제약도 있는 선택입니다.

여기서 꼭 같이 봐야 하는 개념이 phantom dependency입니다.

예를 들어 어떤 패키지 A가 실제로는 lodash를 직접 dependency에 선언하지 않았는데도, 우연한 hoisting 덕분에 동작하는 경우가 있습니다.

// packages/a/src/index.ts
import debounce from 'lodash/debounce';

이 코드가 돌아간다고 해서 A가 올바른 것은 아닙니다. 단지 다른 패키지가 설치한 lodash를 우연히 같이 보고 있을 가능성이 있습니다.

전통적인 node_modules 구조에서는 이런 문제가 늦게 드러날 수 있습니다. 반면 PnP는 패키지가 선언한 의존성만 접근할 수 있게 더 엄격하게 막기 때문에, 이런 잘못된 구조를 빨리 발견하기 쉽습니다.

이 점은 장기적으로는 큰 장점이지만, 레거시 저장소에서는 처음 도입할 때 상당한 정리 비용으로 돌아오기도 합니다.

5. 모노레포와 워크스페이스 운영

모노레포에서는 패키지 매니저 선택의 영향이 훨씬 크게 드러납니다.

특히 중요해지는 것은:

  • workspace 연결 방식
  • 의존성 중복 설치 최소화
  • 패키지 간 경계 유지
  • 태스크 러너와의 조합

pnpm은 이 지점에서 장점이 비교적 뚜렷합니다.

  • workspace 구성이 단순하고
  • 설치 구조가 일관되며
  • 디스크 효율이 좋고
  • Turborepo, Nx 같은 도구와도 현실적으로 잘 맞습니다

반면 Yarn Berry는 더 강한 정책을 걸 수 있지만, 레거시 패키지나 개발자 로컬 환경 차이까지 같이 관리해야 하므로 팀 숙련도가 더 필요합니다.

기술적으로는 workspace:* 같은 프로토콜을 어떻게 관리하느냐도 중요합니다. 다만 이 문법은 pnpmYarn Berry에서 특히 적극적으로 쓰이는 방식이고, npm workspace는 보통 같은 패키지 이름을 직접 의존성에 선언하는 방식으로 연결합니다.

{
  "dependencies": {
    "@repo/ui": "workspace:*"
  }
}

이런 선언은 패키지 간 경계를 명확하게 하고, "로컬 패키지를 레지스트리 버전처럼 착각해서 설치하는 문제"를 줄이는 데 도움이 됩니다. 다만 이 예시는 pnpmYarn Berry 기준으로 이해하는 것이 맞습니다.

또 하나 중요한 것은 hoisting 전략입니다. 모노레포가 커질수록:

  • 어떤 의존성이 루트에 올라오는가
  • peer dependency 충돌이 어디서 발생하는가
  • 특정 패키지의 테스트 환경이 루트 의존성에 우연히 기대고 있지 않은가

가 반복적으로 문제를 일으킵니다.

이 지점에서 pnpmYarn Berry는 둘 다 "의존성 경계를 더 엄격하게 보게 만든다"는 공통점이 있지만, pnpmnode_modules 생태계를 유지하고, Yarn Berry(PnP)는 해석 방식까지 바꾼다는 차이가 있습니다.

6. IDE/도구/라이브러리 호환성

실무에서 생각보다 자주 부딪히는 건 여기입니다.

  • 어떤 CLI가 node_modules를 가정하고 있고
  • 어떤 패키지가 암묵적 hoisting에 기대고 있으며
  • 어떤 에디터 설정이 별도 SDK 없이는 작동하지 않습니다

npmpnpm은 이 지점에서 비교적 안정적입니다.

Yarn Berry(PnP)는 장점이 있지만, 아래 같은 운영 포인트가 함께 따라옵니다.

yarn dlx @yarnpkg/sdks vscode

또는 레거시 패키지의 잘못된 의존성 선언을 packageExtensions로 보정해야 할 수 있습니다.

# .yarnrc.yml
packageExtensions:
  'some-legacy-package@*':
    dependencies:
      'webpack': '*'

즉, PnP는 문제가 일찍 드러난다는 장점이 있지만, 팀이 그 문제를 직접 다룰 수 있어야 한다는 뜻이기도 합니다.

여기서 중요한 것은 "호환성이 높다/낮다"보다, 문제가 생겼을 때 팀이 어디까지 직접 손댈 수 있는가입니다.

예를 들면:

  • 번들러 설정을 조정할 수 있는가
  • editor SDK 문서를 팀에 배포할 수 있는가
  • 레거시 패키지의 dependency 선언 문제를 packageExtensions로 보완할 수 있는가
  • 네이티브 모듈과 postinstall 스크립트가 많은 저장소를 다룰 수 있는가

이런 역량이 있다면 PnP도 충분히 운영할 수 있습니다. 하지만 그런 운영 여력이 없다면, pnpm처럼 더 완만한 절충안을 고르는 편이 부담이 적을 수 있습니다.

7. 온보딩과 팀 운영 비용

패키지 매니저는 팀 문서화 수준과도 연결됩니다.

아래 질문이 중요합니다.

  • 새로 합류한 개발자가 한 줄 명령으로 바로 시작할 수 있는가
  • Windows/macOS/Linux 차이를 팀이 관리할 수 있는가
  • 에디터 설정, SDK, 캐시 클리어 방법이 문서화돼 있는가

이 관점에서는:

  • npm: 설명 비용이 가장 낮음
  • pnpm: 약간의 학습만 있으면 운영하기 쉬움
  • Yarn Berry(PnP): 팀 전체 규칙이 정리돼 있으면 강하지만, 그렇지 않으면 초반 friction이 큼

즉, 더 좋은 도구를 찾기보다 조직이 관리 가능한 복잡도인지를 보는 편이 현실적입니다.

Yarn Berry(PnP)를 더 깊게 보면

Yarn Berry를 단순히 "Yarn의 최신 버전"으로 보면 오해하기 쉽습니다. 실제로는 패키지 설치와 의존성 해석을 더 엄격하게 통제하는 운영 모델에 가깝습니다.

어떤 점이 좋은가

첫째, 잘못된 의존성 사용을 비교적 일찍 드러냅니다.

예를 들어 어떤 패키지가 직접 선언하지 않은 의존성을 우연히 hoisting 덕분에 쓰고 있었다면, PnP에서는 문제가 더 일찍 드러납니다. 장기적으로는 이런 엄격함이 코드베이스 정리에 도움이 됩니다.

둘째, zero-install 같은 전략을 통해 설치 단계를 운영적으로 다르게 볼 수 있습니다. 즉, 캐시를 저장소에 포함해 CI나 신규 개발 환경에서 설치 자체를 최소화하는 방식입니다.

셋째, constraints와 정책화를 통해 "팀이 허용하는 의존성 구조"를 더 강하게 통제할 수 있습니다.

예를 들어 특정 패키지만 특정 버전을 쓰게 제한하거나, 잘못된 workspace 의존성 선언을 CI에서 막는 식의 운영도 가능합니다. 저장소가 커질수록 이런 특성은 개발 편의보다 운영 규칙 관리 측면에서 의미가 커집니다.

어떤 점이 어려운가

반대로 운영이 까다로워지는 이유도 있습니다.

  • 레거시 도구가 node_modules를 당연하게 가정할 수 있고
  • IDE 설정이 별도로 필요할 수 있으며
  • 팀 내 경험치가 쌓이기 전까지는 문제 해결 비용이 큽니다

즉, Yarn Berry는 "더 빠른 패키지 매니저"가 아니라 더 엄격한 운영 체계라고 보는 편이 맞습니다.

기술적으로는 특히 아래 지점에서 마찰이 생기기 쉽습니다.

  • peer dependency 경고가 더 빨리 드러남
  • 레거시 CLI가 파일 시스템 구조를 직접 탐색함
  • 일부 코드 생성 도구가 node_modules 경로를 가정함
  • 로컬 스크립트와 IDE 플러그인이 PnP 해석을 모름

그래서 도입 전에는 "PnP가 좋은가?"보다 "우리 팀이 이 제약을 장기적으로 관리할 수 있는가?"를 먼저 보는 편이 좋습니다.

어떤 팀에 잘 맞을까

아래 조건이 맞으면 잘 맞을 수 있습니다.

  • 모노레포가 크고
  • 의존성 규율이 중요하며
  • 팀이 툴링 문제를 스스로 해결할 수 있고
  • 문서화와 에디터 설정 관리가 잘 되어 있는 경우

반대로 아래 조건이면 부담이 커질 수 있습니다.

  • 레거시 패키지가 많고
  • 신규 인력 온보딩 속도가 매우 중요하며
  • IDE/CLI 호환성 이슈에 시간을 쓰기 어려운 경우

팀 상황별로 보면

1. 단일 앱 + 빠른 온보딩이 중요한 팀

이 경우에는 npm이 가장 현실적인 기본값일 수 있습니다.

  • 별도 학습 비용이 낮고
  • 문서와 예제가 많으며
  • 문제를 만났을 때 검색 가능한 해답도 가장 풍부하기 때문입니다

2. 모노레포 + 디스크/설치 효율이 중요한 팀

이 경우에는 pnpm이 비교적 균형이 좋습니다.

  • workspace 운영이 좋고
  • 설치/디스크 효율이 좋으며
  • 생태계 호환성도 높은 편입니다

3. 정책 강한 레포 + 엄격한 의존성 관리가 중요한 팀

이 경우에는 Yarn Berry(PnP)가 설득력 있습니다.

  • 암묵적 의존성을 줄이고
  • 팀 규칙을 강하게 유지하며
  • zero-install, constraints 같은 전략을 실제 운영 규칙으로 연결할 수 있기 때문입니다

4. 레거시 의존성이 많고 호환성 리스크가 큰 팀

이 경우에는 npm 또는 pnpm이 더 현실적일 수 있습니다.

  • 운영 복잡도를 크게 늘리지 않으면서
  • 기존 생태계와의 마찰을 줄이기 쉽기 때문입니다

5. CI 비용과 빌드 시간을 줄여야 하는 팀

이 경우에는 pnpmYarn Berry를 더 진지하게 볼 만합니다.

다만 선택 기준은 달라집니다.

  • pnpm: 현실적인 효율 개선
  • Yarn Berry: 정책 강도까지 포함한 구조 개선

코드와 설정으로 보면

npm

{
  "packageManager": "npm@10.8.2",
  "scripts": {
    "install:ci": "npm ci"
  }
}

pnpm

{
  "packageManager": "pnpm@10.0.0"
}
# pnpm-workspace.yaml
packages:
  - apps/*
  - packages/*
pnpm install --frozen-lockfile
pnpm -r build

Yarn Berry (PnP)

{
  "packageManager": "yarn@4.6.0"
}
# .yarnrc.yml
nodeLinker: pnp
enableGlobalCache: true
yarn install --immutable
yarn dlx @yarnpkg/sdks vscode

의존성 규칙이 드러나는 예시

암묵적 의존성 문제가 있을 때 체감 차이는 이런 식으로 나타날 수 있습니다. 아래 workspace:* 예시는 pnpm 또는 Yarn Berry 기준으로 보면 됩니다.

// packages/app/package.json
{
  "name": "@repo/app",
  "dependencies": {
    "@repo/ui": "workspace:*"
  }
}
// packages/ui/package.json
{
  "name": "@repo/ui",
  "dependencies": {}
}
// packages/ui/src/index.ts
import clsx from 'clsx';

clsx를 직접 선언하지 않았는데도 어떤 환경에서는 우연히 동작할 수 있습니다. 이런 문제는 Yarn Berry(PnP)에서는 더 강하게 차단되는 편이고, pnpm도 hoisting 설정과 실행 문맥에 따라 더 이른 시점에 드러날 가능성이 있습니다.

이 차이는 불편하게 느껴질 수 있지만, 장기적으로는 저장소 구조를 더 명확하게 만드는 데 도움이 되기도 합니다.

이 예시만 봐도 각 도구가 중요하게 보는 기준 차이가 드러납니다.

  • npm: 가장 기본적인 표준
  • pnpm: workspace와 운영 효율 중심
  • Yarn Berry: 정책과 구조 제어 중심

정리하면

패키지 매니저 선택은 속도 경쟁이 아닙니다. 실제로는 아래 균형을 어떻게 잡을지에 더 가깝습니다.

  • 설치 속도
  • 디스크 효율
  • CI 재현성
  • 생태계 호환성
  • 팀 온보딩 비용
  • 모노레포 운영 복잡도
  • 정책 강도와 조직 성숙도

제 경험 기준으로 요약하면 이렇습니다.

  • 기본값으로는 npm: 가장 익숙하고 도입 부담이 적음
  • 운영 효율까지 보면 pnpm: 균형이 좋은 선택지가 되는 경우가 많음
  • 강한 규율과 정책이 필요하면 Yarn Berry(PnP): 장점은 있지만, 팀 전체가 그 제약을 함께 관리할 수 있어야 함

중요한 것은 "가장 빠른 패키지 매니저"를 찾는 것이 아니라, 우리 팀이 관리할 수 있는 운영 복잡도와 잘 맞는 패키지 매니저를 고르는 것입니다.