개발/Frontend

pnpm vs yarn 차이와 선택 기준

반응형

새 프로젝트 시작할 때 패키지 매니저로 뭘 쓸지 한 번씩 고민하게 되잖아요.

 

npm은 기본이라 빼놓더라도, pnpm이랑 yarn 둘 중에 뭐가 더 나은지 정리해본 적이 없어서 찾아보니 생각보다 차이가 꽤 크더라구요. 결론부터 말하면 2026년 기준으로는 pnpm이 점점 표준 자리를 잡고 있고, yarn은 berry(v2 이상)로 가면서 결이 좀 달라졌어요.

 

이 글에서는 pnpm yarn 차이를 설치 구조, 속도, 디스크 사용량, 모노레포 측면에서 정리해볼게요.

 

어떻게 다른 도구가 됐나

npm이 한참 동안 느리고 락 파일도 불안정하던 시절에 yarn classic(v1)이 나왔어요. 빠르고 결정적인 락 파일을 들고 와서 빠르게 점유율을 가져갔거든요. 그러다 npm이 v5부터 package-lock.json을 도입하면서 격차가 좁혀졌고, pnpm은 아예 디스크 구조 자체를 바꾸는 방식으로 등장했어요.

 

지금 시점에서 보면 노선이 세 갈래로 나뉜다고 보면 돼요.

  • npm: 가장 기본, 안정성 위주
  • yarn: classic은 사실상 유지보수만, berry(v4)는 PnP/Zero-Install 같은 독자 노선
  • pnpm: content-addressable store + symlink 구조로 디스크/속도 둘 다 잡음

node_modules 구조가 다르다

pnpm yarn 차이를 한 줄로 요약하면 node_modules를 만드는 방식이 다르다는 거예요. 이게 거의 모든 차이의 뿌리거든요.

 

yarn(classic)의 경우 npm이랑 비슷한 flat 구조예요. 의존성을 끌어올려서(hoisting) node_modules 최상단에 평평하게 펼쳐놔요.

 

그래서 직접 의존성으로 선언하지 않은 패키지도 require/import가 그냥 되는 경우가 생겨요. 이게 편하긴 한데, "phantom dependency"라고 해서 나중에 의존성을 정리할 때 골치 아픈 원인이에요.

 

pnpm은 완전히 다른 방식이에요. 글로벌 스토어에 패키지 실물 하나만 두고, 프로젝트 node_modules에는 symlink만 깔아요. 게다가 hoisting을 안 해서, 선언한 의존성만 import할 수 있어요. package.json에 적힌 것 외에는 접근 자체가 안 막혀 있는 거죠.

 

yarn berry(v2+)는 또 다른 길로 갔어요. PnP(Plug'n'Play) 모드에서는 node_modules 자체를 안 만들어요. 대신 .pnp.cjs라는 단일 파일에 의존성 매핑을 저장하고, Node의 모듈 해석을 통째로 가로채요. 빠르긴 한데 호환성 이슈가 생각보다 많아서 도입 장벽이 좀 있어요.

 

디스크 사용량 pnpm이 압도적

여러 프로젝트를 굴리는 사람이라면 이게 진짜 체감되는 부분이에요.

 

yarn classic이나 npm은 프로젝트마다 node_modules에 패키지 실물을 따로 복사해요. 같은 react 18버전을 쓰는 프로젝트가 10개면, react 폴더가 10번 복사되는 셈이거든요.

 

pnpm은 글로벌 스토어(~/.pnpm-store)에 패키지를 한 번만 저장하고, 프로젝트마다 하드링크 또는 symlink로 연결해요. 그래서 같은 버전 패키지를 여러 프로젝트가 공유하면 디스크에 한 번만 차지해요. 실제로 모노레포나 여러 클라이언트 프로젝트를 굴리는 환경에서는 디스크 사용량이 절반 이하로 줄어드는 경우가 흔해요.

 

yarn berry도 캐시는 공유하지만, 설치 시 압축된 zip을 그대로 두고 PnP가 거기서 읽는 방식이라 메커니즘이 또 달라요.

 

설치 속도

벤치마크는 환경에 따라 갈리는데, 대략적인 경향은 이래요.

  • 캐시 없는 첫 설치(cold install): pnpm ≈ yarn berry > yarn classic > npm
  • 캐시 있는 재설치(warm install): pnpm이 가장 빠른 편. yarn berry는 PnP면 거의 즉시
  • CI 환경: pnpm이 캐시 적중 시 가장 안정적으로 빠름

핵심은 pnpm이 패키지를 "다시 복사"하지 않고 링크만 만들어서 I/O가 적다는 점이에요. 큰 모노레포에서는 이 차이가 분 단위로 벌어지기도 해요.

 

다만 실측은 프로젝트마다 다르니까, 진지하게 바꿀 거면 본인 프로젝트에서 time pnpm install --frozen-lockfile 같은 식으로 직접 재봐야 해요.

 

모노레포 지원

요즘은 모노레포 쓰는 곳이 늘었잖아요. 이 부분도 차이가 명확해요.

 

yarn workspaces는 yarn classic 시절부터 있었어서 자료가 많고 익숙한 사람이 많아요. berry로 가면 nmHoistingLimits 같은 설정으로 좀 더 세밀하게 조절할 수 있구요.

 

pnpm workspacespnpm-workspace.yaml로 정의해요. 패키지 간 참조는 workspace:* 프로토콜로 명시적으로 적어줘야 하고, 의존성 격리가 기본이라 패키지가 자기 의존성 외의 것을 끌어 쓰는 실수를 차단해줘요. 큰 모노레포에서 의존성이 꼬이는 사고를 막아주는 게 실무에서 꽤 큰 장점이에요.

 

Turborepo, Nx 같은 빌드 시스템도 둘 다 잘 지원하는데, 최근 공식 가이드들은 pnpm을 디폴트로 안내하는 경향이 늘었어요.

 

락 파일과 결정성

락 파일 이름이랑 포맷이 달라요.

  • yarn classic: yarn.lock (커스텀 포맷)
  • yarn berry: yarn.lock (포맷은 비슷하지만 v2 이상은 약간 다름)
  • pnpm: pnpm-lock.yaml (YAML)

 

세 개 다 결정적 설치(같은 락 파일이면 같은 트리)를 보장해요. 다만 pnpm의 YAML이 사람 눈으로 읽기는 가장 편한 편이에요. 의존성 트리가 어떻게 펼쳐졌는지 직접 들여다보기 쉽거든요.

 

CI에서는 --frozen-lockfile(pnpm)이나 --immutable(yarn berry)을 꼭 붙여야 해요. 안 그러면 락 파일이 슬쩍 바뀌는 경우가 생겨요.

 

호환성 이슈

이게 의외로 실무에서 발목 잡는 부분이에요.

 

pnpm은 엄격한 의존성 격리 때문에, phantom dependency에 기대고 있던 일부 라이브러리에서 모듈을 못 찾는 에러가 나기도 해요. 그럴 땐 .npmrcpublic-hoist-pattern[]=*eslint* 같은 호이스팅 패턴을 지정해주거나, shamefully-hoist=true로 전체 hoisting을 켤 수 있어요. 다만 hoisting을 켜면 pnpm 쓰는 장점이 절반 정도 사라지긴 해요.

 

yarn berry PnP는 호환성 이슈가 좀 더 큰 편이에요. Node 모듈 해석을 가로채는 방식이라 일부 도구(특히 네이티브 바이너리, 일부 빌드 도구)가 안 돌아가는 경우가 있거든요. node_modules 모드(nodeLinker: node-modules)로 빠지면 호환성 문제는 거의 해결되는데, 그러면 yarn berry를 쓰는 이유 중 하나가 사라져요.

 

호환성만 보면 pnpm > yarn berry PnP 순서가 일반적이에요.

 

어떤 걸 쓰면 좋을까

결론부터 말하면 이렇게 정리할 수 있어요.

  • 신규 프로젝트, 모노레포 가능성 있음: pnpm을 디폴트로
  • 기존에 yarn classic 잘 쓰고 있는 프로젝트: 굳이 안 바꿔도 됨. 단 새 기능 도입 시 berry 또는 pnpm 검토
  • 디스크/CI 비용 줄이고 싶음: pnpm
  • 가장 빠른 설치를 원함 + 호환성 이슈 감내 가능: yarn berry PnP
  • 사내 표준이 yarn berry로 정해진 곳: nodeLinker를 node-modules로 두면 무난

 

개인적으로는 작년부터 신규 프로젝트는 거의 다 pnpm으로 가는 편이에요. 디스크 절약이 체감되고, 의존성 실수를 일찍 잡아주는 게 장기적으로 이득이더라구요. 다만 팀에 처음 도입할 때는 phantom dependency 에러 대응 가이드를 한 번 정리해두는 게 좋아요. 익숙해지기 전까지는 "왜 갑자기 모듈을 못 찾지" 하는 순간이 한두 번씩 와요.

 

벤치마크 숫자보다 중요한 건, 본인 프로젝트가 처한 제약(CI 시간, 디스크, 모노레포 여부, 팀 숙련도)이에요. 그 기준에서 보면 pnpm이 가장 무난한 선택지가 되는 경우가 많아졌다 정도로 정리하면 될 것 같아요.

반응형