[Gihub Actions] 캐시 액션으로 패키지 설치 최적화

 

 

Github Actions를 이용해 CI/CD 파이프라인을 설정하는 과정에서 중요한 부분 중 하나는 패키지 설치 및 빌드 시간을 최적화하는 것이다.

특히 Github Actions를 활용해 자동화된 배포를 설정하다 보면 의존성 설치 단계나 빌드 단계에서 꽤 많은 시간이 소요되는 경우가 많다.

프로젝트의 규모가 커질수록 이런 문제는 더욱 두드러진다.

예를 들어, 리액트 프로젝트에서 CRA를 사용하면 빌드 시간이 점점 늘어나는 것을 경험하게 된다.

https://itguswjd.tistory.com/214

 

[React] CRA에서 Vite로 마이그레이션하기

지금까지 리액트 프로젝트를 진행하면서 대부분 CRA로 생성하였다.이전 프로젝트들의 크기 및 파일 개수는 상대적으로 많지 않았지만 1년째 진행중인 프로젝트는 파일 개수가 점점 많아지면서

itguswjd.tistory.com

실제로 위의 포스팅에서 소개했듯이 프로젝트이 규모가 점점 커져 기존 CRA로 생성한 리액트 프로젝트의 경우 빌드 시간이 증가하는 문제가 있었다. 하지만 이는 Vite로 마이그레이션을 하면서 약 19s에서 9s로 시간을 단축할 수 있었다.

하지만 빌드 시간은 단축하였지만 여전히 Github Actions로 배포가 완료되기 까지 약 2분이 소요되는 문제가 있었다.

 

실제로 위 설정에서 npm install이 약 1분 30초나 걸리는 경우가 발생했다.

이는 워크플로우 실행 속도를 느리게 만들어 개발 피드백 루프에 지연을 일으키는 문제로 이어졌다.

이 문제를 해결하기 위해, GitHub Actions에서 캐시 액션을 사용하여 이미 설치된 패키지들을 저장하고 재활용하는 방법을 적용하였다.

캐시가 설정되면 동일한 의존성을 매번 새로 설치하지 않고, 기존 캐시에서 불러오기 때문에 설치 시간을 크게 줄일 수 있는 것이다.

이번 글에서는 GitHub Actions 캐시 액션을 사용해 패키지 설치 시간을 최적화하는 방법과 그 효과에 대해 살펴보려고 한다.

 

 

 

01. 캐시 액션이란?

Github Actions의 캐시 액션은 워크플로우 실행 시 의존성 파일이나 빌드 결과 등을 캐싱하여 이후 워크플로우 실행 속도를 최적화하는 기능이다. 일반적으로 CI/CD 파이프라인에셔는 빌드와 테스트를 수행하기 전에 각종 의존성(ex. npm 모듈)을 설치해야 한다.

하지만 이 작업은 프로젝트가 크거나 의존성이 많을수록 상당히 시간이 걸린다.

캐시 액션을 활용하면 한 번 설치한 의존성을 저장해두었다가, 이후 동일한 환경에서 동일한 의존성을 필요로 할 때 기존 캐시에서 불러올 수 있어 설치 시간을 줄일 수 있다.

 

💡 캐시 액션 장점

  • 속도 향상: 중복된 작업(의존성 설치, 빌드 결과 생성)을 캐시에서 불러와 반복 수행하지 않으므로 워크플로우 실행 속도가 크게 개선
  • 비용 절감: 클라우드 환경에서 시간은 곧 비용이므로, 불필요한 설치나 빌드 작업을 줄여서 전체 실행 비용을 낮출 수 있음
  • 개발 생산성 향상: 워크플로우 속도가 빨라짐에 따라 코드 피드백이 더 빠르게 제공되므로, 개발자가 코드 수정 후 결과를 빨리 확인할 수 있어 개발 효율성 향상

 

 

02. 캐시 액션 사용하기

아래 yaml 예시는 캐시 액션을 적용한 Github Actions의 설정 파일이다.

name: MajorIn

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4  # 소스 코드 체크아웃

      - name: Cache node modules
        uses: actions/cache@v4  # 캐시 액션을 사용하여 node_modules 캐싱
        with:
          path: node_modules  # 캐시 대상 디렉토리로 node_modules 설정
          key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}  # OS 정보와 package-lock.json 해시값 기반의 고유 키 생성
          restore-keys: |
            ${{ runner.OS }}-build-
            ${{ runner.OS }}-
 

 

#1. Cache 액션 사용 (uses: actions/cache@v4)

actions/cache@v4GitHub에서 제공하는 캐시 액션으로, 주로 node_modules, 빌드된 바이너리 파일, 혹은 다운로드된 종속성 파일 등을 저장하여, 동일한 워크플로우 내에서 반복적인 작업을 피하고 속도를 높이기 위해 사용된다.

 

#2. path 설정 

path는 캐시할 폴더나 파일의 경로를 지정하는 옵션이다.

여기서는 node_modules 디렉토리를 지정하여, 패키지 설치 작업이 매번 실행되지 않도록 한다.

node_modules 폴더가 캐시에 존재하면, 워크플로우는 이를 사용해 패키지를 다시 설치하지 않고, 바로 캐시된 폴더를 사용하게 된다.

 

#3. key 설정

key는 캐시를 식별하기 위한 고유한 값이다.

예시의 key 설정은 ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }} 형태로 이루어지며, 이를 통해 운영체제와 package-lock.json의 해시값에 따라 캐시가 달라지게 된다.

runner.OS는 워크플로우가 실행 중인 운영체제의 이름을 반환한다. 이 정보는 OS별로 캐시를 구분해 사용하기 위한 것이다.

hashFiles('**/package-lock.json')는 package-lock.json 파일의 해시값을 기반으로 캐시를 생성한다.

이로써 package-lock.json의 변경 사항이 있을 경우, 새로운 캐시가 생성된다.

 

#4. restore-keys 설정

restore-keys 옵션은 key 값과 정확히 일치하는 캐시가 없을 때, 유사한 키의 캐시를 복원하도록 돕는다.

이 예시에서는 ${{ runner.OS }}-build-와 ${{ runner.OS }}- 두 가지의 하위 키를 설정해 두었다.

  • ${{ runner.OS }}-build-는 빌드 환경에 따라 동일한 캐시를 사용할 수 있도록 설정한다.
  • ${{ runner.OS }}-는 OS만 같은 경우도 캐시를 복원할 수 있도록 설정하여, 가능한 캐시 복원 성공률을 높인다.

 

 

 

03. 캐시 히트 여부 출력값 활용

GitHub Actions의 actions/cache 액션은 cache-hit라는 출력값을 통해 특정 캐시 키에 해당하는 데이터가 존재하는지 여부를 확인할 수 있다. 이 기능을 통해, 캐시 히트 여부에 따라 후속 작업의 실행 조건을 설정하거나, 캐시가 존재할 때 불필요한 작업을 생략할 수 있다. 다음 코드 예시에서는 cache-hit 값을 활용하여 실행 로그에 캐시 히트 여부를 출력하는 방법을 보여준다.

 

name: MajorIn

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source code. # 레포지토리 체크아웃
        uses: actions/checkout@v4

      - name: Cache node modules # node modules 캐싱
        uses: actions/cache@v4
        with:
          path: node_modules
          key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.OS }}-build-
            ${{ runner.OS }}-

      - name: Set up Node.js
        uses: actions/setup-node@v3 # 최신 버전으로 업데이트
        with:
          node-version: "18.20.4" # 사용 중인 Node.js 버전 설정

      - name: .env setting
        run: |
          echo "VITE_APP_SERVER_URL=${{ secrets.VITE_APP_SERVER_URL }}" >> .env
          echo "VITE_APP_AI_URL=${{ secrets.VITE_APP_AI_URL }}" >> .env

      - name: Install Dependencies # 의존 파일 설치
        if: steps.cache.outputs.cache-hit != 'true' # 중복 제거
        run: npm install

 

위 yaml 코드는 cache-hit 값을 활용하여 캐시 히트 여부를 확인하고, 이를 기반으로 npm install 명령을 조건부로 실행한다.
 
 

💡캐시 히트 여부에 따른 조건부 실행

if: steps.cache.outputs.cache-hit != 'true' 구문은 캐시가 히트되지 않았을 때만 npm install을 실행하도록 한다.

즉, cache-hit이 'true'이면 캐시된 node_modules가 사용되므로 npm install은 실행되지 않으며, 캐시가 없는 경우 npm install을 통해 의존성을 새로 설치하게 된다.

 
  • 캐시 히트: 캐시가 이미 존재한다면, steps.cache.outputs.cache-hit의 값은 'true'로 설정되고, npm install은 실행되지 않는다.
  • 캐시 미스: 캐시가 없으면, steps.cache.outputs.cache-hit의 값은 'false'로 설정되고, npm install이 실행된다.

 

 

 

04. 캐시 히트 여부 적용 결과 (1m 32s -> 2s)

처음 cache-hit를 적용했을 때는 캐시가 존재하지 않아 의존성 설치가 이루어졌기 때문에 배포 시간에 큰 변화가 없었다.

그러나 두 번째 적용 후, 의존성 설치 시간이 1분 32초에서 단 2초로 대폭 단축되었다.

이는 캐시가 히트되어 이미 설치된 node_modules를 재사용했기 때문이다.

 

캐시 히트는 의존성 설치 시간을 크게 줄여주어, 워크플로우의 전체 실행 시간을 단축시켰다. 특히, 의존성 설치와 같은 반복적인 작업에서 캐시를 활용하면, 불필요한 작업을 피하고 효율성을 높일 수 있음을 실질적으로 확인할 수 있었다. 

 

 


References

https://www.daleseo.com/github-actions-cache/

 

GitHub Actions의 캐시(Cache) 액션으로 패키지 설치 최적화하기

Engineering Blog by Dale Seo

www.daleseo.com

https://blog.banksalad.com/tech/github-action-npm-cache/

 

반응형