GitHub Actions 무료 러너 시간(2,000분) 절약을 위한 빌드 캐싱 기법과 실행 속도 단축 가이드
GitHub Actions는 현대적인 DevOps 파이프라인을 구축하는 데 가장 강력하고 편리한 도구입니다. 하지만 기본으로 주어지는 Private 리포지토리의 무료 러너 시간(월 2,000분)은 프로젝트 규모가 커지고 개발자가 늘어남에 따라 순식간에 고갈되곤 합니다. 하루에 10분짜리 빌드가 10번만 돌아도 한 달이면 3,000분입니다. 이미 무료 제공량을 초과해 추가 비용을 지불하고 있거나, 빌드가 끝날 때까지 모니터를 멍하니 바라보고 있다면 파이프라인 최적화가 시급한 시점입니다.
GitHub Actions 무료 러너 시간을 아끼는 가장 핵심적인 방법은 **‘동일한 작업을 반복하지 않는 것’**입니다. 매 빌드마다 수백 메가바이트에 달하는 라이브러리를 새로 다운로드하고, 변경되지도 않은 도커 레이어를 처음부터 다시 빌드하는 비효율을 제거해야 합니다. 본 글에서는 캐싱 기법을 중심으로 GitHub Actions의 속도를 극적으로 끌어올리고 무료 러너 분수를 아끼는 실전 가이드를 소개합니다.
1. 왜 우리의 CI/CD는 매번 느릴까? 호스트 러너의 동작 원리 이해
GitHub Actions의 호스트 러너(Hosted Runner)는 빌드가 시작될 때마다 완전히 새로운 가상 머신(VM)을 할당합니다. 이 무결한 환경은 이전 빌드의 찌꺼기가 남아 발생할 수 있는 부작용을 방지해 주지만, 치명적인 단점이 있습니다. 이전 빌드에서 이미 다운로드했던 종속성(node_modules, Gradle 캐시 등)을 완전히 새로 받아야 한다는 점입니다.
이로 인해 발생하는 비효율을 시각화하면 다음과 같습니다.
| 빌드 단계 | 캐싱 미적용 시 특징 | 캐싱 적용 시 개선 효과 |
|---|---|---|
| 환경 준비 (VM Setup) | 패키지 매니저 설치 및 초기화 | 변경 없음 (고정 시간 소모) |
| 의존성 다운로드 | 매번 네트워크를 통해 외부에서 전체 다운로드 (느림) | 로컬(GitHub 내부망) 캐시 저장소에서 즉시 복원 (빠름) |
| 빌드 및 컴파일 | 소스 코드 전체 빌드 | 변경된 파일 및 캐싱된 컴파일 아티팩트 활용 |
| 도커 이미지 생성 | 모든 레이어 순차적 빌드 | 캐싱된 레이어 재사용 (gha 캐시 활용) |
결국 성능 최적화의 핵심은 네트워크 I/O와 무의미한 CPU 연산을 줄여 전체 빌드 시간을 단축하고, 결과적으로 분 단위로 과금되는 러너 점유 시간을 절약하는 것에 있습니다.
2. 주요 기술 스택별 actions/cache 적용 가이드
의존성 캐싱을 적용하는 가장 범용적인 방법은 GitHub에서 공식 제공하는 actions/cache 액션을 사용하는 것입니다. 그러나 최근에는 각 언어별 셋업 액션(actions/setup-*) 내부에 캐싱 기능이 내장되어 있어 더욱 간결하게 설정할 수 있습니다.
Node.js (npm / yarn / pnpm) 캐싱 최적화
Node.js 환경의 node_modules는 대표적인 ’용량 괴물’입니다. 프로젝트의 패키지 잠금 파일(package-lock.json 등)이 변경되지 않았다면, 이전 빌드의 캐시를 그대로 재사용해야 합니다.
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
# setup-node의 자체 캐시 기능 활성화
cache: 'npm'
# pnpm의 경우 'pnpm', yarn의 경우 'yarn' 지정 가능
- name: Install dependencies
run: npm ci
cache: 'npm' 설정을 추가하는 것만으로 내부적으로 ~/.npm 디렉토리를 package-lock.json 해시값을 키로 삼아 캐싱합니다. 패키지가 추가되거나 변경되면 해시값이 바뀌어 캐시를 새로 생성하고, 그렇지 않으면 캐시를 복원하여 npm ci 속도를 수 배 이상 단축합니다.
Java / Kotlin (Gradle) 캐싱 최적화
Gradle 빌드는 의존성 라이브러리뿐만 아니라 컴파일된 빌드 출력물도 캐싱할 수 있어 최적화 효과가 더욱 큽니다.
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
# setup-java의 자체 gradle 캐시 기능 활성화
cache: 'gradle'
- name: Build with Gradle
run: ./gradlew build -x test
setup-java 액션은 Gradle의 종속성 데이터 및 빌드 캐시를 자동으로 추적합니다. 다만 Gradle 캐시는 매 빌드마다 메타데이터가 미세하게 변경되어 캐시가 불필요하게 자주 업데이트되는 경향이 있으므로, 사용하지 않는 임시 파일을 빌드 프로세스 종료 전에 청소해 주는 러너 환경 구성을 고려할 수도 있습니다.
Docker 빌드 레이어 캐싱 (gha 캐시엔진 활용)
도커 이미지를 빌드하고 레지스트리에 푸시하는 과정은 캐싱이 없을 때 무지막지하게 긴 시간이 소요됩니다. docker/build-push-action은 GitHub Actions 전용 캐시 백엔드인 gha 타입을 지원합니다.
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: user/app:latest
# GitHub Actions 전용 캐시 엔진(gha) 활용
cache-from: type=gha
cache-to: type=gha,mode=max
cache-to: type=gha,mode=max 옵션은 빌드 프로세스의 모든 중간 레이어를 GitHub Actions 캐시 저장소로 업로드합니다. 이후 빌드에서는 변경되지 않은 레이어를 빌드하지 않고 GitHub 캐시에서 직접 가져와 재사용하므로, 무거운 스프링 부트나 프론트엔드 빌드 레이어가 포함된 도커 빌드 시간을 극적으로 줄여줍니다.
3. 캐싱 도입 전후 빌드 지표를 직접 비교하는 법
위와 같은 캐싱 설정을 도입했을 때 실제로 빌드 속도가 얼마나 단축되고 러너 시간이 얼마나 절약되는지는 프로젝트의 의존성 규모, 도커 이미지 레이어 구성, 빌드 단계 수에 따라 크게 달라집니다. 일반화된 단축률을 제시하기보다, 도입 전 최근 몇 차례 빌드의 소요 시간을 GitHub Actions의 Actions 탭에서 직접 확인해 기록해 두고, 캐싱 적용 후 동일한 방식으로 비교해 보는 것이 정확합니다.
러너 가동 시간이 줄어들면 그만큼 한 달에 주어진 2,000분을 더 많은 빌드에 쓸 수 있게 되므로, 무료 한도 초과로 인한 추가 결제 빈도도 함께 낮아집니다.
4. 고수들의 한 끗 차이: 캐시 이외의 시간 절약 꿀팁
캐시 설정만으로도 큰 효과를 보지만, 워크플로 구성 자체를 영리하게 설계하면 추가적인 시간 낭비를 막을 수 있습니다.
1) 불필요한 파일 변경 시 빌드 방지 (Path Filtering)
마크다운 문서(README.md)나 설정 문서가 수정되었을 때 전체 테스트 코드가 돌아가고 도커 빌드가 수행될 필요는 없습니다. 특정 경로의 파일이 바뀔 때만 워크플로가 트리거되도록 설정하세요.
on:
push:
branches: [ "main" ]
paths-ignore:
- '**.md'
- 'docs/**'
- '.gitignore'
2) 중복 워크플로 즉시 취소 (Concurrency Control)
풀 리퀘스트(PR)를 보내고 코드를 급히 수정해 다시 푸시하는 경우, 이전에 실행 중이던 빌드는 더 이상 의미가 없습니다. 자동으로 이전 빌드를 종료하도록 concurrency 옵션을 정의하십시오.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
새로운 커밋이 푸시되면, 실행 중이던 이전 커밋의 빌드 잡(Job)이 즉시 취소(cancelled)되므로 불필요하게 낭비되는 러너 시간을 즉각적으로 절약할 수 있습니다.
5. 캐싱 사용 시 주의사항 및 트러블슈팅
빌드 캐시가 만병통치약은 아닙니다. 캐시 저장 용량과 네트워크 오버헤드로 인한 병목을 이해하지 못하면 오히려 빌드가 더 느려지거나 꼬일 수 있습니다.
- GitHub Actions 캐시 한계 용량 (10GB): 리포지토리당 최대 10GB의 캐시를 저장할 수 있습니다. 한도를 초과하면 가장 오래된 캐시부터 순차적으로 자동 삭제(Eviction)됩니다. 따라서 빌드 결과물이나 도커 캐시가 너무 거대하다면 캐시 만료 주기를 직접 관리하거나 캐시 범위를 좁혀야 합니다.
- 캐시 오버헤드 (네트워크 복원 속도 vs 설치 속도): 의존성 크기가 너무 큰 반면(예: 수 기가바이트의 딥러닝 모델 파일) 패키지 매니저의 설치 속도가 빠르다면, 캐시를 GitHub 서버로부터 압축 해제하며 다운로드하는 네트워크 시간보다 새로 설치하는 속도가 더 빠를 수 있습니다. 캐시 적용 전후의 복원 단계 소요 시간(
Restore cache)을 반드시 확인하세요. - 독성이 있는 캐시 깨기 (Cache Busting): 가끔 종속성 트리가 꼬여 잘못된 캐시가 빌드 에러를 유발할 수 있습니다. 이 경우 워크플로 YAML 내의 캐시 키 이름에 접두사를 붙여(
v1-에서v2-로 수정 등) 캐시 키를 명시적으로 무효화(Busting)해야 합니다.
GitHub Actions 최적화는 단순히 인프라 비용을 아끼는 행위를 넘어, 개발자 피드백 루프를 가속하는 가치 있는 작업입니다. 빌드 결과를 받기 위해 기다리는 10분이 3분으로 줄어들 때 개발 몰입도는 비약적으로 향상됩니다. 지금 바로 사용 중인 리포지토리의 Actions 탭으로 이동해 빌드 타임라인을 점검하고, 무겁게 쌓여 있는 의존성 다운로드 단계를 캐싱 체계로 전환해 보시기 바랍니다.