원격 캐시는 있는데 왜 재사용은 안 될까?

원격 캐시 자체는 문제가 아니다. 문제는 캐시 키가 toolchain·플랫폼·환경·경로 중 하나만 달라져도 완전히 다른 키로 취급된다는 점이다. 캐시가 "있다"는 사실과 "재사용된다"는 사실은 다른 명제이며, 이 둘을 구분하지 못하면 겉보기엔 정상인데 매번 콜드 컴파일이 도는 상황을 못 잡아낸다.

핵심 정리

  • 캐시 키에 toolchain·플랫폼·환경·경로가 빠지면 히트율은 사실상 0으로 수렴한다.
  • sccache는 RUSTC_WRAPPER=sccache로 rustc를 감싸는 구조라, rustc 버전 불일치나 wrapper 비활성화가 가장 흔한 히트 실패 원인이다.
  • rust-toolchain.toml로 버전을 고정하고 CI·로컬을 동일하게 맞추는 것이 재사용을 유지하는 최소 조건이다.
  • 로컬에서 FROM-CACHE가 떴다는 사실만으로는 원격 캐시가 진짜 작동하는지 증명되지 않는다.
  • 캐시 키를 완벽히 맞춰도 의존성 그래프 자체가 크고 불안정하면 증분성 이득은 제한된다.

두 선택지를 나란히 놓아보자. 한쪽은 원격 캐시 서버를 붙이고 sccache만 켠 구성이다. 다른 쪽은 rust-toolchain.toml로 버전을 고정하고, 체크아웃 경로·환경변수·타깃 플랫폼까지 CI와 로컬을 맞춘 구성이다. 둘 다 "원격 캐시를 쓴다"고 말하지만, 실제 히트율은 완전히 다른 이야기가 된다.

toolchain 버전이 다르면 캐시는 정말 무효가 될까?

그렇다. sccache는 캐시 키에 컴파일러 버전을 포함시키기 때문에, CI 러너와 로컬 머신이 서로 다른 rustc 버전을 쓰면 같은 소스·같은 플래그라도 키가 갈라진다. 2026년 기준 실무 가이드에서 반복적으로 지적되는 원인 순위에서 rustc 버전 불일치가 상위에 오르는 이유도 이 때문이다. 특히 여러 브랜치나 여러 러너 이미지를 섞어 쓰는 파이프라인에서는, 이미지가 업데이트될 때마다 toolchain 버전이 슬쩍 바뀌면서 캐시가 조용히 무효화되는 경우가 흔하다. rust-toolchain.toml로 버전을 명시적으로 고정하면 CI와 로컬이 항상 같은 rustc를 받게 되고, 이게 재사용을 지키는 가장 값싼 조치다.

작업 경로와 환경변수도 캐시 키에 들어갈까?

들어간다. 공유 가능한 캐시의 전제 조건은 명령·입력·toolchain·플랫폼·환경을 키에 포함하고, 네트워크·시간·호스트명 같은 비결정적 요소는 배제하는 것이다. 반대로 말하면 체크아웃 경로가 러너마다 다르거나(/home/runner/work/... vs /builds/...), 링커·SDK 버전이 이미지마다 다르면 겉으로는 같은 커밋을 빌드해도 키가 갈라진다. 이 지점이 대조의 핵심이다. 캐시 서버를 붙이는 것과 캐시가 실제로 재사용되는 것 사이에는, 경로·환경변수·플랫폼을 하나하나 맞추는 별도의 작업이 들어간다.

증분 빌드와 sccache를 같이 쓰면 무슨 일이 일어날까?

충돌한다. 2026년 Rust CI 사례에서는 CARGO_INCREMENTAL=0을 함께 지정하는 패턴이 반복해서 등장하는데, 이는 증분 빌드 산출물과 sccache의 캐시 항목이 서로 겹치면서 캐시 무효화를 유발하기 때문이다. 즉 "증분 빌드도 켜고 원격 캐시도 켠다"는 구성은 직관적으로 더 빠를 것 같지만, 실제로는 두 메커니즘이 서로의 전제를 깨는 조합이 될 수 있다. 공유 스토리지 백엔드와 sccache를 조합해 cold compile을 캐시 읽기로 바꾸는 사례도, 증분 빌드를 끄고 sccache 쪽 재사용에 전적으로 의존하는 구성을 전제로 한다.

로컬에서 뜬 FROM-CACHE, 원격에서도 믿을 수 있을까?

믿을 수 없다. 로컬 캐시 히트는 같은 머신·같은 세션 안에서의 결과일 뿐, 다른 머신·다른 파이프라인에서도 같은 키가 재현된다는 증거가 아니다. 진짜 검증은 두 개의 깨끗한 환경을 준비해서, 같은 toolchain으로 한쪽에서 빌드해 캐시에 저장하고 다른 쪽에서 그 캐시를 읽어오는 방식으로 확인해야 한다. sccache --show-stats로 히트/미스 카운트를 직접 찍어보는 것도 이 검증의 일부다.(https://github.com/mozilla/sccache) 이 절차를 생략하고 "캐시를 붙였으니 빨라졌겠지"라고 넘기면, 실제로는 매번 콜드 컴파일이 도는데도 그 사실을 놓치게 된다.

두 구성을 나란히 놓으면 뭐가 갈릴까?

아래는 같은 프로젝트를 두고 "키 미고정 구성"과 "키 고정 구성"을 대조한 것이다. 수치보다 조건 자체가 비교의 핵심이다.

항목 키 미고정 구성 키 고정 구성
toolchain 버전 CI 러너 이미지에 딸린 버전 그대로 사용 rust-toolchain.toml로 명시 고정
작업 경로/환경 러너마다 다른 체크아웃 경로 방치 명령·입력·플랫폼·환경까지 키에 포함
증분 빌드 설정 CARGO_INCREMENTAL 기본값 유지 필요 시 CARGO_INCREMENTAL=0으로 sccache와 충돌 회피
히트 검증 방식 로컬 FROM-CACHE 로그만 확인 두 개의 분리된 환경에서 저장→읽기 재현
실패 시 증상 캐시는 있는데 매번 콜드 컴파일 재현 가능한 히트, --show-stats로 확인 가능

이 표에서 갈리는 건 "캐시를 쓰느냐"가 아니라 "캐시 키가 재현 가능하게 설계됐느냐"다.

키를 고정하는 데 실제로 드는 비용은?

작지 않다. rust-toolchain.toml을 도입하려면 기존에 러너 이미지 버전에 암묵적으로 의존하던 CI 설정을 걷어내고, 로컬 개발 환경도 같은 파일을 따르도록 맞춰야 한다. 체크아웃 경로를 통일하려면 CI 파이프라인 스크립트 자체를 수정해야 하는 경우도 있고, CARGO_INCREMINTAL 전환은 증분 빌드에 의존하던 로컬 반복 개발 사이클의 체감 속도를 일부 포기하는 트레이드오프를 동반한다. 여기에 더해, 500k LOC 규모에서 incremental compilation이 65% 빌드 시간을 줄였다거나 -Zincremental-parallel로 메모리를 25% 줄였다는 수치는 2차 자료 기반이라 버전·하드웨어·워크로드·cold/warm 여부를 확인하지 않고 그대로 옮기면 재현되지 않을 수 있다. 전환 비용은 설정 변경 자체보다, 이 설정들이 실제로 히트율을 올렸는지 검증하는 절차를 파이프라인에 상시로 넣는 데서 발생한다.

그래서 언제 키 관리에 공을 들여야 할까?

정답은 프로젝트 규모와 CI 매트릭스 복잡도에 달려 있다. crate 수가 적고 CI 러너 이미지가 고정돼 있다면, rust-toolchain.toml 하나만 추가해도 충분한 경우가 많다. 반면 여러 플랫폼·여러 이미지·여러 브랜치를 동시에 빌드하는 매트릭스라면, 경로·환경변수까지 맞추는 검증 절차 없이는 캐시 히트율을 신뢰할 수 없다. Rust 프로젝트 목표 문서가 incremental system 재설계를 별도 과제로 두고, 서로 다른 rustc 인자 사이에 더 잘 공유되는 공통 베이스를 지향하는 것도 이 키 불안정성이 컴파일러 차원에서도 아직 완전히 해소되지 않았다는 방증이다.(https://rust-lang.github.io/rust-project-goals/) 또한 2025년 설문에서 의존성 수 감소·feature 축소·crate 분할이 상위 개선 수단으로 꼽힌 만큼, 캐시 키를 아무리 잘 맞춰도 입력 그래프 자체가 불안정하면 이득은 제한된다는 점을 기준으로 삼는 게 맞다.

핵심 정리

  • 캐시 키에는 toolchain·플랫폼·환경·경로가 모두 들어가야 하며, 하나라도 빠지면 히트율은 0에 수렴한다.
  • sccache의 히트 실패는 대부분 rustc 버전 불일치, wrapper 비활성화, 잘못된 캐시 복원 경로에서 나온다.
  • CARGO_INCREMENTAL=0은 증분 빌드와 원격 캐시의 충돌을 피하는 실무 패턴으로 반복 등장한다.
  • 로컬 FROM-CACHE 확인만으로는 원격 재사용을 증명할 수 없고, 두 환경 간 저장·읽기 재현이 필요하다.
  • 키를 완벽히 맞춰도 의존성 그래프가 크고 불안정하면 증분성 이득은 근본적으로 제한된다.

자주 묻는 질문

sccache 히트율이 갑자기 떨어졌다면 가장 먼저 뭘 봐야 할까?

CI 러너 이미지가 업데이트되면서 rustc 버전이 바뀌었는지부터 확인하는 게 순서다. rust-toolchain.toml이 있는데도 히트율이 떨어졌다면 체크아웃 경로나 환경변수가 이미지 교체와 함께 바뀌었을 가능성을 다음으로 본다.

로컬에서는 캐시가 잘 되는데 CI에서만 안 될 때는?

로컬과 CI가 같은 toolchain·환경을 쓰는지부터 대조해야 한다. FROM-CACHE 로그가 로컬에서만 뜨는 건 검증이 아니므로, 두 환경 간 저장·읽기를 직접 재현하는 절차를 CI 파이프라인에 넣어보는 게 확인 방법이다.

증분 빌드를 끄면 로컬 개발 속도가 느려지지 않을까?

그럴 수 있다. CARGO_INCREMENTAL=0은 원격 캐시와의 충돌을 줄이기 위한 CI 쪽 설정으로 주로 쓰이며, 로컬 반복 개발에는 증분 빌드를 그대로 유지하고 CI에서만 분리하는 방식이 일반적인 절충안이다.

캐시 키를 다 맞췄는데도 빌드가 안 빨라지면?

그 경우엔 캐시 문제가 아니라 의존성 그래프 자체의 문제일 가능성이 크다. crate 수와 활성 feature를 줄이거나 큰 crate를 분할하는 것이 캐시 설정보다 더 근본적인 개선 수단으로 꼽힌다.

더 알아보기

빌드·번들 툴체인 완전 가이드: 증분성·캐시·병렬성의 측정 — 이 주제의 종합 가이드