Next.js CI Build Caching
배경 이야기
Next.js를 도커라이징한 이미지를 github actions를 통해 aws ec2에 실행 및 배포한 과정에서 CI/CD 경량화 할 수 있는 방법이 없을까 고민하고 개선했던 과정을 작성하게 되었다.
github actions의 캐시(cache) 액션으로
패키지 설치,next.js 빌드최적화 하기
github actions의 캐시(cache) 액션으로 패키지 설치, next.js 빌드 최적화 하기
로컬 환경에서는 최초에 외부 패키지 설치와 빌드 명령을 하면, 이후 반복 작업 시 캐시를 활용해 속도를 크게 단축할 수 있지만, CI runner 환경에서는 실행될 때마다 새로운 인스턴스가 생성되므로 매번 새로 불필요한 재설치를 하게 된다.
먼저 로컬환경에서 node_modules와 .next/cache의 유무 차이로 패키지 설치 및 빌드 시간차가 발생하는지 확인하면 좋을 것 같다.
로컬에서 각, 패키지 설치와 빌드 타임을 최초 실행과 그 다음 실행시간 비교
1. 외부 패키지 설치
로컬에서 node_modules를 제거한 후, 최초 패키치 설치와 그 다음 설치 시간을 비교한다.
$ rm -r node_modules
$ time npm install
$ time npm install
최초 설치와 그 다음 설치 시간을 비교해보면 5.751s -> 1.025s 단축되는 것을 확인할 수 있다.
2. nextjs build
로컬에서 .next/cache를 제거한 후, 최초 빌드와 그 다음 빌드 시간을 비교한다.
rm -r .next/cache
time npm run build
time npm run build
빌드 시간이 15.249s -> 10.103s 로 줄었다.
이전에 말했듯, actions runner에서는 매번 새로운 인스턴스가 생성되므로 node_modules와 .next/cache가 존재하지 않으므로 이 둘을 actions 캐싱하여 npm install, npm run build 속도를 줄이도록 하려한다.
캐싱방법은 두가지가 있다.
.gitignore에node_modules와.next를 포함시키지 않고 github에 push시키고,actions/checkout@v3를 통해 내려받는 방법- https://github.com/actions/cache/blob/main/examples.md#node---npm, Next.js Continuous Integration (CI) Build Caching #github-actions,
actions-cache를 사용하여.npm,.next/cache처리하기
첫번째 방법은 '그냥 순순히 패키지 설치와, 빌드에 필요한 컨텐츠들을 깃에 올려버리면 어떨까?'하고 생각해본 내용이다. 하지만, git 캐싱하면 clone 속도가 느려지고, 협업하는 과정에서 부담이 커져 비효율적이므로 적합하지 않다.
두 번째 방법으로 actions/cache를 써서 필요한 파일만 부분적으로 캐싱하고 빠르게 복원할 수 있다. 본래의 actions runner 환경의 캐시 문제이므로 actions/cache가 적합하다고 판단해서 이 방식을 채택했다.
주의:
node_moudles를 캐싱하는 것은 권장하지 않는 사항이다.actions/cache 문서를 보면
node_modules를 캐싱하는 것을 권장하지 않는다. 사용하는 노드 버전에 따라 문제가 발생할 수 있으며,npm ci명령어 동작이 안된다 한다. npm ci 명령어를 사용할 경우, npm ci는 node_modules를 제거하고 다시 설치하기 때문에 불필요한 캐싱이기 때문이다.구글링을 통해 대안방법을 모색했고, 관례적으로 npm-cache를 활용하여 외부 패키지 설치시간을 줄이는 것으로 확인했다. npm-cache 문서를 통해 알 수 있듯이,
~/.npm은 npm 자체에서 사용하는 캐시 파일이 저장되는 경로다. 이 경로는npm install이나npm ci명령어로 패키지를 설치할 때 사용되는 캐시된 데이터를 보관하며, 설치 속도를 크게 향상시킬 수 있다. ~/.npm에는 실제 실행 시 필요한 node_modules 디렉토리는 포함되지 않지만, 대신 압축된 형태의 패키지 파일이 저장되어 있어, 네트워크 요청을 줄이고 중복 다운로드를 방지해준다. 이 캐시를 활용하면, 동일한 패키지를 여러 번 설치하거나 CI 환경에서 의존성을 관리할 때 효율적으로 패키지를 설치할 수 있다.
runner 환경 .npm, .next/cache 캐싱
- name: Get npm cache directory
id: npm-cache-dir
shell: bash
run: echo "dir=$(npm config get cache)" >> ${GITHUB_OUTPUT}
- name: Cache npm
uses: actions/cache@v4
id: npm-cache
with:
path: ${{ steps.npm-cache-dir.outputs.dir }}
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-- name: Cache .next/cache
uses: actions/cache@v4
id: 'next-cache'
with:
path: ${{ github.workspace }}/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-runner 환경에서 패키지 설치, 빌드 결과
결과: Install packages 16s, build 26s
결과: Install packages 12s, build 18s
결과적으로 다음과 같이 단축된 시간을 확인할 수 있었다.
- 패키지 설치
16s->12s단축 - next.js 빌드
26s->18s단축