특정 저장소나 도구 설정이 아니라, CI 성능을 판단할 때의 순서를 정리한 글이다.
CI가 느리다는 이야기가 나오면 패키지 매니저가 후보로 올라온다.
“pnpm이라서 느린 것 아닐까?”
“Yarn으로 바꾸면 빨라질까?”
질문 자체는 자연스럽다. 하지만 패키지 매니저 교체는 lockfile, 워크스페이스, 개발 환경, CI 설정, 의존성 해석 방식까지 건드리는 변화다. 설치 시간이 전체 병목인지 확인하기 전에 시작하면, 큰 비용을 내고도 체감 성능은 그대로일 수 있다.
먼저 물어야 할 것은 “무엇으로 바꿀까?”가 아니라 “어디에서 시간이 사라지는가?”다.
이 질문은 머지큐에서도 같다. 최신 조합을 누가 검증할지와 설치·실행 비용의 병목을 한 규칙으로 뭉개면, 결국 개발자가 수동으로 최신 상태를 맞추는 방식으로 돌아가기 쉽다. 머지큐를 도입하기 전에 main부터 정의해야 했다
한 줄 요약
pnpm은 공유 store와 링크 구조로 설치·디스크 중복을 줄이는 강점이 있지만, CI store 캐싱이 항상 설치 시간을 줄이는 것은 아니다.- Yarn의 PnP나 Zero-Install은 다른 선택지를 제공하지만, 모노레포·도구 호환성·운영 비용까지 함께 검증해야 한다.
- 패키지 매니저 전환은 설치가 병목이라는 측정 뒤에 판단할 일이다. 그 전에는 설치, 태스크 실행, 러너 대기, merge queue 검증을 분리해 봐야 한다.
“CI가 느리다”는 하나의 시간이 아니다
파이프라인의 총 시간은 대개 다음의 합이다.
대기 시간
+ 러너 초기화
+ 의존성 설치
+ build / typecheck / test
+ 아티팩트 업로드와 후처리
여기서 패키지 매니저가 직접 다루는 것은 대부분 의존성 설치 구간이다. 러너가 배정되기까지 기다리는 시간이나, 타입 체크와 테스트가 실행되는 시간, 머지큐가 최신 조합을 다시 검증하는 시간은 다른 문제다.
이 구분이 없으면 다음과 같은 결론이 쉽게 나온다.
CI가 느리다 → 설치가 느릴 것이다 → 패키지 매니저를 바꾸자.
실제로 설치가 전체의 작은 비중이고 테스트가 대부분을 차지한다면, 이 전환은 성능 문제를 풀지 못한다. 반대로 매번 깨끗한 러너에서 같은 의존성을 내려받고 있다면, 패키지 매니저를 바꾸기 전에 캐시 복원 경로와 키부터 점검해야 한다.
pnpm이 잘하는 일과, 보장하지 않는 일
pnpm은 패키지 파일을 content-addressable store에 한 번 저장하고 프로젝트의 node_modules에는 hard link와 symlink로 연결한다. 같은 버전의 의존성을 여러 프로젝트가 쓰는 경우 디스크 중복과 재다운로드를 줄이는 구조다. 모노레포에서 매력적인 이유다. pnpm: how it works
다만 이 구조가 “CI install은 항상 빠르다”를 뜻하지는 않는다.
pnpm의 CI 문서도 store 캐싱이 설치 속도를 높인다고 보장하지 않는다고 명시한다. 캐시를 복원하고 압축을 풀고, lockfile을 확인하고, 링크를 만드는 비용이 새 runner의 다운로드 비용보다 클 수도 있다. 신뢰하지 않는 job이 신뢰하는 job의 store를 오염시키지 않도록 캐시 쓰기 권한도 분리해야 한다. pnpm CI 문서
즉 pnpm 환경에서 먼저 확인할 것은 “pnpm을 계속 쓸까?”가 아니라 다음이다.
- 캐시가 실제로 복원되는가?
- lockfile과 런타임 버전이 일관적인가?
- cold / warm runner에서 install 시간이 각각 얼마인가?
- store 캐시의 다운로드·압축 해제 비용은 얼마인가?
- 설치 뒤 실행되는 build·typecheck·test보다 설치 비중이 큰가?
Yarn이 제공하는 다른 선택지
Yarn Berry는 PnP와 Zero-Install 같은 선택지를 제공한다. PnP는 전통적인 node_modules 구성 방식을 피할 수 있고, Zero-Install은 필요한 cache 산출물을 저장소에 포함해 fresh checkout에서도 설치 단계를 줄이는 접근이다. Yarn Q&A: Zero-Install과 PnP
이것은 분명 유효한 설계 선택이다. 다만 “Yarn이 더 빠르다”만으로 결정하기에는 질문이 더 있다.
- 현재 사용하는 bundler, test runner, IDE, 배포 도구가 PnP와 잘 맞는가?
- 의존성을 암묵적으로 참조하던 코드가 드러났을 때 고칠 준비가 되어 있는가?
.yarn/cache를 저장소에 둘 때 저장소 크기와 변경 관리 비용은 감당할 만한가?- 개발자 환경과 CI에서 같은 linker 전략을 유지할 것인가?
- 전환 기간에 lockfile과 패키지 설치 정책을 어떻게 관리할 것인가?
이 질문에 답하지 않은 패키지 매니저 전환은 CI 개선 작업이 아니라 생태계 마이그레이션이다. 실제 성능 이득이 있더라도, 그 이득이 전환과 운영 비용을 이기는지는 별도의 판단이다.
캐시는 서로 대체하지 않는다
CI 개선 논의에서 자주 섞이는 캐시를 나눠 보면 판단이 쉬워진다.
| 층 | 재사용하는 것 | 먼저 볼 지표 |
|---|---|---|
| 의존성 캐시 | 패키지 파일과 메타데이터 | install cold/warm 시간, hit rate, 캐시 복원 시간 |
| 태스크 캐시 | build·lint·typecheck·test 산출물 | 태스크별 hit rate, 저장·복원 시간, 정확성 |
| 컨테이너·러너 이미지 | 런타임과 도구 초기화 | runner 준비 시간, image pull 시간 |
| 머지큐 결과 재사용 | 동일한 소스 tree의 검증 결과 | 동일 tree 비율, merge group 재검증 비율 |
태스크 캐시의 대표적인 예가 Turborepo의 원격 캐시다. 같은 task 입력과 설정으로 만든 산출물을 팀의 로컬 환경과 CI가 함께 재사용하도록 만든다. 다만 이는 설치 캐시가 아니라 task 결과 캐시다. 원격 캐시 서버에 접근할 권한, 신뢰할 수 있는 토큰 전달, runner와 네트워크 경로, 안전하게 캐시할 수 있는 task 선언이 모두 갖춰져야 한다. Turborepo remote caching
태스크 캐시가 잘 되어도 설치 캐시가 느릴 수 있다. 설치가 빨라도 merge group이 달라지면 큐 검증은 다시 필요하다. 캐시 서버에 접근할 수 없거나 러너 아키텍처가 달라져 복원 비용이 커질 수도 있다.
그래서 “Turbo 원격 캐시를 못 쓰니 Yarn으로 바꾸자”처럼 층을 건너뛰는 결론은 위험하다. 원격 태스크 캐시의 접근 제약과 패키지 설치 방식은 같은 문제가 아니다. 전자는 task 산출물을 어디서 어떤 신뢰 경계로 재사용할지의 문제이고, 후자는 의존성을 어떻게 해석하고 설치할지의 문제다.
전환 전에 남길 측정표
패키지 매니저 전환을 제안하기 전에, 적어도 다음 표는 있어야 한다.
| 구간 | cold p50 / p90 | warm p50 / p90 | 캐시 hit rate | 개선 후보 |
|---|---|---|---|---|
| 러너 대기 | - | 러너 수, 우선순위, 큐 정책 | ||
| 환경 초기화 | 이미지·도구 | 이미지, 사전 설치 | ||
| 의존성 설치 | store·metadata | 캐시 키, 패키지 매니저 | ||
| build | task | 영향 범위, 산출물 캐시 | ||
| typecheck / test | task | 분할 실행, 선택적 검증 | ||
| 머지큐 검증 | tree 동일성 | merge group 정책 |
여기서 설치가 전체의 큰 비중이고, pnpm store 캐시를 올바르게 써도 충분히 줄지 않으며, Yarn의 특정 linker 전략이 현재 도구 체인과 호환된다는 검증까지 끝났다면 그때 전환을 비교할 수 있다.
그 전에는 더 작은 개선이 많다. 캐시 키 수정, 신뢰 경계 분리, runner 이미지 정리, 영향 범위 기반 태스크 실행, 불필요한 워크플로 제거 같은 것들이다.
마치며
pnpm과 Yarn 중 하나가 보편적으로 더 낫다는 결론은 없다. 프로젝트의 의존성 구조, 개발 환경, CI runner, 보안 정책, 모노레포의 크기에 따라 답이 달라진다.
중요한 것은 전환의 순서다. 패키지 매니저는 설치 문제를 푸는 도구다. CI 전체가 느린 문제를 푸는 이름이 아니다.
먼저 시간을 층별로 나누고, 병목을 확인하고, 가장 작은 변경부터 검증하자. 패키지 매니저 전환은 그 뒤에도 남는 설치 문제에 대한 선택지여야 한다.