안녕하세요 여러분! 오늘은 정말 중요한 주제인 CI 환경에서의 빌드 캐싱(Build Caching)에 대해 같이 공식 문서를 살펴보려고 해요. 현업에 가시면 CI/CD 파이프라인을 구축하는 일이 잦은데, 이때 캐싱을 제대로 안 해두면 빌드할 때마다 세월아 네월아 기다려야 한답니다. 자, 그럼 공식 문서를 한 줄씩 번역하면서 제 실무 팁도 팍팍 얹어드릴게요!
빌드 성능을 획기적으로 향상시키기 위해서, Next.js는 빌드 간에 공유될 수 있도록 .next/cache 폴더에 캐시를 저장해 둬요. 매번 처음부터 모든 걸 다시 만들지 않겠다는 똑똑한 전략이죠!
지속적 통합(CI) 환경에서 이 캐시의 장점을 100% 활용하려면, 여러분의 CI 워크플로우가 빌드와 빌드 사이에도 캐시를 날리지 않고 잘 유지(persist)하도록 올바르게 설정되어야 해요.
💡 강사의 주의사항!
만약 여러분의 CI가 빌드 간에.next/cache를 유지하도록 설정되지 않았다면, 아주 흔하게 캐시 감지 안 됨 (No Cache Detected) 에러를 마주하시게 될 거예요. 이러면 Next.js가 제공하는 빠른 빌드 속도를 다 깎아먹게 됩니다!
자, 그럼 우리가 실무에서 자주 쓰는 대표적인 CI 제공업체(Provider)별로 캐시를 어떻게 설정하는지 예시를 하나씩 살펴볼까요?
Next.js를 만든 곳이 Vercel이다 보니, Vercel에서는 Next.js 캐싱이 여러분을 위해 아주 자동으로, 완벽하게 설정되어 있습니다. 여러분이 추가로 무언가 조치를 취하실 필요가 전혀 없어요! 참 편하죠?
만약 Vercel에서 모노레포 도구인 Turborepo를 사용하고 계신다면, 이곳에서 더 자세한 내용을 확인해 보세요.
CircleCI를 쓰신다면 .circleci/config.yml 파일 안에 있는 save_cache 스텝(step)을 수정해서 .next/cache 경로를 포함시켜 주시면 됩니다:
steps:
- save_cache:
key: dependency-cache-{{ checksum "yarn.lock" }}
paths:
- ./node_modules
- ./.next/cache
만약 프로젝트에 save_cache 키가 아직 설정되어 있지 않다면, CircleCI의 빌드 캐싱 설정에 관한 공식 문서를 꼭 먼저 따라 해보세요.
🧑🏫 강사 팁: 보시다시피
node_modules도 같이 캐시를 잡아주는 게 국룰이에요. 의존성 패키지 설치 시간도 만만치 않거든요!
Travis CI의 경우, .travis.yml 파일에 다음 내용을 추가하거나 기존 내용에 병합(merge)해 주세요:
cache:
directories:
- $HOME/.cache/yarn
- node_modules
- .next/cache
GitLab을 사용하는 회사도 꽤 많죠? 여러분의 .gitlab-ci.yml 파일에 다음 코드를 추가하거나 병합해 주세요:
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .next/cache/
🧑🏫 강사 팁: 여기서
CI_COMMIT_REF_SLUG변수는 보통 브랜치 이름을 뜻해요. 즉 브랜치별로 캐시를 따로 관리하겠다는 의미가 된답니다.
Netlify에서는 Netlify Plugins (넷리파이 플러그인)과 함께 @netlify/plugin-nextjs 패키지를 사용하시면 됩니다. Vercel 다음으로 세팅이 아주 간편한 편이에요.
엔터프라이즈 환경에서 많이 쓰는 AWS CodeBuild입니다. buildspec.yml 파일에 다음 내용을 추가(또는 병합)해 주세요:
cache:
paths:
- 'node_modules/**/*' # 더 빠른 `yarn` 또는 `npm i`를 위해 `node_modules`를 캐시합니다.
- '.next/cache/**/*' # 애플리케이션의 더 빠른 리빌드를 위해 Next.js 캐시를 저장합니다.
요즘 오픈소스나 많은 스타트업에서 대세로 쓰고 있는 GitHub Actions입니다! 저도 정말 애용하는데요. GitHub의 actions/cache 액션을 사용해서, 여러분의 워크플로우 파일(yaml)에 다음 스텝을 추가해 주시면 됩니다:
uses: actions/cache@v4
with:
# `yarn`, `bun` 또는 기타 패키지 매니저를 활용한 캐싱에 대해서는 여기([https://github.com/actions/cache/blob/main/examples.md)를](https://github.com/actions/cache/blob/main/examples.md)를) 참조하시거나, actions/setup-node([https://github.com/actions/setup-node)를](https://github.com/actions/setup-node)를) 통한 캐싱을 활용하실 수도 있습니다.
path: |
~/.npm
${{ github.workspace }}/.next/cache
# 패키지나 소스 파일이 변경될 때마다 새로운 캐시를 생성합니다.
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
# 소스 파일은 변경되었지만 패키지는 변경되지 않았다면, 이전 캐시로부터 복원(rebuild)합니다.
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
🧑🏫 강사 팁: 여기서 눈여겨보실 점은
hashFiles를 사용해서package-lock.json이나 소스코드의 변경 사항을 감지하여 캐시 키(key)를 갱신한다는 거예요. 이렇게 하면 의존성이나 코드가 바뀌었을 때 오래된 캐시를 써서 발생하는 버그를 막을 수 있답니다!
Bitbucket을 쓰신다면, bitbucket-pipelines.yml 파일의 최상단(pipelines와 동일한 레벨)에 다음 내용을 추가하거나 병합해 주세요:
definitions:
caches:
nextcache: .next/cache
그런 다음, 여러분의 파이프라인 step 하위의 caches 섹션에서 앞서 정의한 캐시를 참조해 주시면 됩니다:
- step:
name: your_step_name
caches:
- node
- nextcache
Heroku의 커스텀 캐시(custom cache) 기능을 사용해서, 최상단 package.json 파일 안에 cacheDirectories 배열을 추가해 주시면 됩니다:
"cacheDirectories": [".next/cache"]
Azure Pipelines의 캐시 태스크(Cache task)를 사용해서, 파이프라인 yaml 파일에서 next build를 실행하는 태스크 이전 어딘가에 다음 태스크를 추가해 주시면 돼요:
- task: Cache@2
displayName: 'Cache .next/cache'
inputs:
key: next | $(Agent.OS) | yarn.lock
path: '$(System.DefaultWorkingDirectory)/.next/cache'
전통 깊고 강력한 Jenkins입니다! Jenkins의 Job Cacher (잡 캐셔) 플러그인을 사용하여, 평소에 next build나 npm install을 실행하던 Jenkinsfile 영역에 다음 빌드 단계를 추가해 주세요:
stage("Restore npm packages") {
steps {
// GIT_COMMIT 해시값을 기반으로 캐시용 lock-file을 작성합니다.
writeFile file: "next-lock.cache", text: "$GIT_COMMIT"
cache(caches: [
arbitraryFileCache(
path: "node_modules",
includes: "**/*",
cacheValidityDecidingFile: "package-lock.json"
)
]) {
sh "npm install"
}
}
}
stage("Build") {
steps {
// GIT_COMMIT 해시값을 기반으로 캐시용 lock-file을 작성합니다.
writeFile file: "next-lock.cache", text: "$GIT_COMMIT"
cache(caches: [
arbitraryFileCache(
path: ".next/cache",
includes: "**/*",
cacheValidityDecidingFile: "next-lock.cache"
)
]) {
// 이른바 `next build` 실행 부분
sh "npm run build"
}
}
}
모든 문서의 의미론적(semantic) 개요를 한눈에 보시려면, https://nextjs.org/docs/sitemap.md를 참고해 주세요.
또한, 이용 가능한 전체 문서의 인덱스는 https://nextjs.org/docs/llms.txt에서 확인하실 수 있습니다.