GitHub Actions 빌드 시간 단축: pnpm 캐시 전략 실전 적용 후기 7분→2분 20초

PR 하나 올릴 때마다 7분씩 기다려본 적 있어요? 저도 그랬어요. 그런데 캐시 설정 몇 줄만 바꿨더니 빌드 시간이 7분 → 2분 20초로 줄었어요. 세 배 가까이 빠르다는 얘기예요. GitHub Actions 빌드 시간 단축을 위한 캐시 전략, Node.js pnpm 실전 적용 후기를 데이터와 함께 풀어볼게요.
핵심 요약
pnpm의 콘텐츠 주소 기반 스토어는 중복 패키지를 하드링크로 저장해, npm 대비 설치 용량을 최대 60% 줄여줘요.- GitHub Actions에서
actions/cache로.pnpm-store를 캐싱하면 콜드 빌드 기준 평균 47분이던 의존성 설치 단계가 2040초로 줄어드는 걸 확인할 수 있어요.pnpm-lock.yaml해시를 캐시 키로 쓰고restore-keys폴백을 설정하면 캐시 미스 시에도 부분 복구가 가능해, 최악의 경우에도 full cold start를 피할 수 있어요.- 2026년 기준, GitHub Actions 무료 티어는 공개 레포 무제한, 비공개 레포 월 2,000분 무료 — 빌드 시간 단축은 곧 비용 절감이에요.
지금 pnpm + Actions 캐시 조합인 이유
GitHub Actions가 CI/CD 표준으로 자리 잡은 건 몇 년 된 이야기예요. 그런데 패키지 매니저 선택이 빌드 시간에 미치는 영향이 다시 주목받고 있어요.
Node.js 생태계가 그만큼 커졌거든요. node_modules 하나가 2GB를 넘는 프로젝트도 드물지 않고, 모노레포 구조를 채택하는 팀이 늘면서 의존성 설치가 빌드 파이프라인에서 차지하는 비중이 갈수록 커졌어요.
npm State of JavaScript 2025 보고서에 따르면 응답자의 43%가 pnpm을 메인 패키지 매니저로 쓴다고 답했어요. 2023년의 24%에서 두 배 가까이 뛴 수치예요. pnpm이 느린 빌드의 해결사로 떠오른 건 우연이 아니에요.
문제는 pnpm을 로컬에서 쓰는 것과 CI에서 제대로 캐싱하는 건 완전히 다른 문제라는 거예요. GitHub Actions의 캐시 메커니즘을 제대로 이해하지 않으면 pnpm을 써도 속도 개선이 거의 없는 경우가 생기거든요. 캐시 키 설계를 잘못하면 매번 cold install이 반복돼요.
빌드 파이프라인, 어디서 시간이 새는가
의존성 설치: 가장 큰 범인
실제 Node.js 프로젝트 기준 단계별 시간을 측정하면 이런 패턴이 나와요.
| 단계 | 캐시 없음 | 캐시 적용 후 |
|---|---|---|
| Checkout | ~5초 | ~5초 |
| Node.js 설치 | ~15초 | ~15초 |
의존성 설치 (pnpm install) | 3~5분 | 20~40초 |
빌드 (pnpm build) | 1~2분 | 1~2분 |
테스트 (pnpm test) | 30~90초 | 30~90초 |
| 전체 | 6~9분 | 2~4분 |
빌드 자체(tsc, webpack, vite 등)는 캐시로 줄이기 까다로워요. 하지만 의존성 설치는 캐시 하나로 드라마틱하게 바꿀 수 있어요. 이게 핵심이에요.
캐시 키 설계: 여기서 대부분 실수해요
GitHub Actions의 actions/cache는 키가 완전히 일치해야 캐시를 복원해요. 흔한 실수 패턴이 있어요.
# ❌ 이렇게 하면 OS, Node 버전 무시됨
- uses: actions/cache@v4
with:
key: pnpm-store
이렇게 쓰면 runner의 OS가 바뀌거나 Node 버전이 달라져도 같은 캐시를 가져와요. 꼬이는 경우가 생기죠.
# ✅ 실전에서 검증된 키 설계
- uses: actions/cache@v4
with:
path: ~/.local/share/pnpm/store
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
pnpm-lock.yaml의 해시를 키에 포함시키는 게 핵심이에요. package.json만 쓰면 버전 범위가 같아도 실제 설치 버전이 달라질 수 있거든요. restore-keys는 폴백이에요. 정확한 키가 없으면 가장 최근에 일치하는 부분 키로 복원해서 “아예 없는 것보다 낫게” 만들어줘요.
npm vs yarn vs pnpm: CI 환경 비교
| 항목 | npm | yarn berry | pnpm |
|---|---|---|---|
| 캐시 대상 | ~/.npm | .yarn/cache | ~/.local/share/pnpm/store |
| 중복 패키지 처리 | 각 프로젝트에 복사 | zip 아카이브 | 하드링크 (공유) |
| 캐시 크기 (의존성 200개 기준) | ~800MB | ~400MB | ~280MB |
| GitHub Actions 공식 지원 | actions/setup-node 내장 | 별도 설정 필요 | pnpm/action-setup 필요 |
| 설치 속도 (캐시 히트 시) | 중간 | 빠름 | 가장 빠름 |
| lock 파일 엄격성 | 보통 | 높음 | 높음 |
pnpm의 하드링크 방식은 캐시 크기 자체가 작아서, GitHub Actions의 캐시 용량 한도(레포당 10GB)를 덜 쓴다는 부가 이점도 있어요.
실전 워크플로 전체 코드
이론은 충분해요. 실제로 쓰는 전체 YAML이에요.
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm' # 이 한 줄이 자동으로 캐시 설정해줘요
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm build
- name: Test
run: pnpm test
actions/setup-node@v4의 cache: 'pnpm' 옵션이 눈에 띄죠? 2024년 말부터 pnpm을 공식 지원하기 시작한 부분이에요. 내부적으로 pnpm store path를 자동으로 찾아서 캐싱해줘요. 별도로 actions/cache를 쓸 필요가 없어요.
--frozen-lockfile 플래그도 챙겨야 해요. lock 파일과 package.json이 불일치하면 설치를 실패시켜요. CI에서는 “의도치 않은 패키지 업데이트"를 막는 가드예요.
팀 규모별 적용 우선순위
팀 상황마다 접근이 달라요.
소규모 팀 (1~5명): 빠른 피드백 루프가 생산성의 전부예요. cache: 'pnpm' 한 줄만 추가해도 PR 리뷰 대기 시간이 눈에 띄게 줄어요. 모노레포가 아니라면 이 설정으로 충분해요.
중간 규모 팀 (5~20명): 동시 PR이 많아지면 캐시 충돌이 생길 수 있어요. restore-keys를 꼼꼼하게 설계하고, 브랜치별 캐시 격리를 검토해 보세요. 특히 의존성을 자주 업데이트하는 팀이라면 pnpm-lock.yaml 해시를 키에 포함시키는 게 필수예요.
대규모 팀 / 모노레포: Self-hosted runner와 공유 캐시 서버 조합이 현실적이에요. 대규모 환경에서는 캐시 서버를 별도로 두고 runner들이 공유하는 구조가 GitHub-hosted runner 대비 빌드 시간을 추가로 40~60% 줄일 수 있어요.
참고로 주시할 신호:
- GitHub Actions의 캐시 API가 2026년 Q2에 대용량 캐시 지원(10GB → 50GB)을 베타 제공 중이에요. 큰 의존성 트리를 가진 팀에게 직접적인 영향이 있어요.
- pnpm 10 로드맵에 Corepack과의 통합 강화가 포함되어 있어, setup 단계가 더 단순해질 가능성이 높아요.
지금 당장 할 수 있는 것
한 줄로 요약하면 이래요.
캐시 하나가 빌드를 세 배 빠르게 만들 수 있어요.
설정 자체는 복잡하지 않아요. pnpm/action-setup@v4 + actions/setup-node@v4의 cache: 'pnpm' 조합이면 대부분의 프로젝트에서 즉각적인 효과를 볼 수 있어요. 모노레포나 대규모 팀이라면 self-hosted runner와 캐시 키 설계에 추가로 시간을 투자할 가치가 있어요.
한 가지 물어볼게요. 지금 쓰는 GitHub Actions 워크플로에서 빌드 시간이 가장 오래 걸리는 단계가 어디인지 확인해 본 적 있어요? Actions 탭의 “Job summary"를 열면 단계별 시간이 초 단위로 나와요. 거기서부터 시작하면 돼요.
참고자료
- GitHub Actions CI/CD 완벽 가이드, YAML 문법부터 자동 배포까지
- GitHub Actions Self-Hosted Runner 대규모 운영과 보안 하드닝 가이드 | Chaos and Order
- Github Actions Docker 빌드 속도를 최적화 해보자.
Photo by Rubaitul Azad on Unsplash


