#Frontend

그때는 맞고 지금은 틀리다. Yarn Berry에서 pnpm으로 패키지 매니저 전환기

그때는 맞고 지금은 틀리다. Yarn Berry에서 pnpm으로 패키지 매니저 전환기
01

Summary

Yarn Berry가 정답인 줄 알았는데... 카카오페이가 pnpm으로 갈아탄 진짜 이유

메모리 스파이크부터 Docker 이미지 경량화까지, 데이터로 증명한 패키지 매니저 마이그레이션

프로젝트의 성장에 따라 과거의 최적화 기법이 오히려 병목이 된 사례를 분석하고, pnpm으로의 전환을 통해 기술적 난제를 해결한 과정을 소개합니다. 단순한 도구 교체를 넘어 실제 배포 지표와 인프라 환경을 고려한 의사결정의 정석을 보여줍니다.

  • 01Yarn PnP의 메모리 적재 방식이 SSR 빌드 시 Worker별 중복 발생으로 OOM을 일으키는 구조적 원인 규명
  • 02pnpm의 심링크 기반 구조와 Next.js Standalone 빌드의 시너지로 도커 이미지 크기 83% 절감
  • 03Zero-installs가 실제 CI 환경에서 네이티브 모듈 때문에 예상보다 느릴 수 있다는 실전 통찰 제공
  • 04성능 지표를 시각화한 대시보드를 구축하여 전사 개발자들의 변화에 대한 심리적 저항을 데이터로 해결

RECOMMENDATION

모노레포 규모가 커지면서 CI/CD 빌드 속도나 메모리 부족 문제를 겪고 있는 프론트엔드 팀에게 강력히 추천합니다. 특히 Next.js 기반의 SSR 서비스를 운영한다면 배포 이미지 최적화 관점에서 pnpm 도입을 검토해야 합니다.

The Problem

과거 사내 레지스트리 불안정 문제를 해결하기 위해 도입했던 Yarn Berry의 Zero-installs 방식이 프로젝트 규모가 커짐에 따라 Git 저장소 비대화, DX 저하, 그리고 SSR 빌드 시 메모리 초과(OOM) 현상을 유발했습니다.

The Solution

유령 의존성 문제를 방지하면서도 표준 node_modules 구조를 활용하는 pnpm으로의 전환을 결정하였으며, 실제 사내 배포 환경과 유사한 조건에서 메모리 점유율 및 설치 속도에 대한 데이터 검증을 거쳐 도입을 확정했습니다.

The Result

도커 이미지 크기가 약 83% 감소하고 최대 메모리 사용량이 약 64% 줄어들었으며, pnpm의 효율적인 설치 구조 덕분에 우려했던 배포 시간 지연 없이 빌드 안정성과 개발 생산성을 동시에 확보했습니다.

Trade-off

Zero-installs의 장점인 의존성 설치 생략 이점을 포기하게 되었으나, 본문에 명시된 대로 실제 네이티브 의존성 재빌드 비용을 고려했을 때 실질적인 배포 속도 차이는 없었으며 전환에 따른 개발자 피로도는 개선된 지표 공유를 통해 완화했습니다.

03

Key Concepts

Concept · 01

Zero-installs

의존성 패키지를 압축 파일 형태로 Git 저장소에 직접 포함시켜 배포 시 별도의 설치 과정을 생략하는 Yarn Berry의 기법입니다.

  • 사내 레지스트리 서버가 불안정할 때 배포 안정성을 확보하기 위해 도입되었습니다.
  • 네이티브 의존성이 포함될 경우 아키텍처 차이로 인해 배포 시 재빌드가 필요하여 완전한 무설치가 어려워지는 한계가 발견되었습니다.
Concept · 02

PnP (Plug and Play)

node_modules를 생성하지 않고 .pnp.cjs 파일을 통해 의존성 위치를 런타임에 해석하여 메모리에 로드하는 방식입니다.

  • 빌드 단계에서 다수의 워커가 동시에 PnP 메타데이터를 메모리에 로드하며 메모리 스파이크를 유발했습니다.
  • 표준 구조를 따르지 않아 IDE 설정이나 디버깅 시 ZipFS 경로 연결 등 개발자 경험을 저해하는 요소가 있었습니다.
Concept · 03

pnpm Symlink 구조

중복된 패키지를 글로벌 스토어에 하나만 저장하고 프로젝트 내 node_modules에는 심링크로 연결하여 공간을 절약하는 구조입니다.

  • 표준 node_modules 방식을 따르므로 Next.js의 의존성 트레이싱 기술과 완벽하게 호환됩니다.
  • 전체 의존성을 메모리에 올리지 않고 필요한 시점에만 모듈을 해석하여 메모리 사용량을 안정적으로 유지합니다.