CI 캐시가 복원됐는데 의존성이 옛것이라면, 캐시 키가 lockfile을 반영하지 않는 것이다
빌드가 “이상하게” 성공하거나 옛 버전으로 나온다면, 캐시가 현재 lockfile과 어긋난 의존성 트리를 되살렸을 가능성이 높다. 캐시 자체가 아니라 캐시 키 설계가 원인이다.
1) 캐시 키에 lockfile 해시가 없다
키가 브랜치명이나 고정 문자열이면 lockfile이 바뀌어도 같은 키로 옛 캐시를 복원한다. 키에는 반드시 lockfile 해시를 넣는다.
key: deps-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
deps-
2) restore-keys 폴백이 stale 캐시를 되살린다
정확한 키가 없으면 restore-keys의 접두어 매칭으로 가장 최근의 다른 캐시를 복원한다. 편리하지만, lockfile이 바뀐 상황에선 부분적으로 낡은 트리를 얹는다. 폴백을 쓸 거면 설치 단계가 반드시 lockfile 기준으로 트리를 재정합해야 한다.
3) node_modules를 통째로 캐시하지 않는다
가장 흔한 근본 원인이다. node_modules 전체를 캐시하면 lockfile과 어긋난 트리가 그대로 살아난다. npm ci는 node_modules를 지우고 lockfile대로 재설치하므로, 캐시는 다운로드 캐시(~/.npm)만 하는 게 안전하다(빠르면서 정합).
4) 캐시가 경계를 넘어 오염된다
OS·아키텍처·언어 버전이 다른 잡끼리 캐시를 공유하면 네이티브 모듈이 깨진다. 키에 runner.os와 버전을 포함한다.
정리
키에 lockfile 해시 → restore-keys는 신중히 → node_modules 대신 다운로드 캐시 → OS/버전 분리. 의심되면 캐시를 한 번 무효화(키 접두어 변경)해 깨끗한 상태와 비교한다.
빠른 진단 체크리스트
- 빌드가 이상하게 성공하면 캐시가 stale 트리를 되살렸는지 본다
- 캐시 키에 lockfile 해시(hashFiles)가 들어갔는지 확인한다
- restore-keys 폴백이 옛 캐시를 되살리는지 점검한다
- node_modules 통째 캐시 대신 다운로드 캐시(~/.npm)만 한다
npm ci로 lockfile 기준 재설치를 강제한다- 캐시 키에 runner.os·언어 버전을 포함한다
- 네이티브 모듈이 OS·아키텍처 간 오염됐는지 본다
- 의심되면 키 접두어를 바꿔 캐시를 무효화하고 비교한다