캐시를 이용하는 방법들
- 개발
- 2026. 1. 9. 21:25
캐시는 보통 읽기 성능을 올리기 위해 사용한다고 알고 있습니다.
하지만 그렇다고 해서 모든 곳에 캐싱을 적용할 수는 없습니다. 캐시는 성능을 얻는 대신, 정합성을 어느 정도 포기해야 하는 구조이기 때문에 무작정 사용하는 건 오히려 리스크가 될 수 있습니다.
또 하나 짚고 넘어가야 할 점은, 캐싱만으로 모든 읽기 문제를 해결하려는 접근은 좋은 전략이 아니라는 점입니다. 캐시는 어디까지나 보조 수단이지, 데이터의 진짜 기준(Source of Truth)을 대체하지는 않습니다.
그렇다면 캐시가 없거나, 캐시 미스가 발생한 경우 데이터는 어디에서 가져와야 할까요? 흔히 RDB에서 가져온다고 생각하기 쉽지만, 이것도 여러 선택지 중 하나일 뿐입니다. 실제로는 데이터의 성격에 따라 검색 엔진, 별도의 조회용 스토어, 외부 서비스 등 다양한 읽기 경로가 존재할 수 있습니다.
결국 중요한 건 "캐시를 쓰느냐 마느냐"가 아니라, 어떤 데이터를 어디에서 읽고, 캐시는 그 과정에서 어떤 역할을 맡을 것인가를 결정하는 일입니다. 이 글에서는 이런 관점에서 캐시 전략을 하나씩 살펴보려고 합니다.
캐시는 왜 탄생하였을까?
캐시는 단순히 빠르게 조회하고 싶어서 생겨난 기술은 아닙니다.
정확히 말하면, DB가 느려서도 아니고 성능 욕심 때문도 아닙니다.
하지만 시간이 지나면서 시스템의 구조는 점점 커지기 시작합니다.
이는 기존 방식, 즉 RDB 중심의 구조만으로는 더 이상 모든 요청을 감당하기 어려워진다는 의미이기도 합니다.
이 지점에서 흔히 떠올릴 수 있는 해결책은 보통 세 가지입니다.
- Read Replica
- 샤딩
- 인덱스 추가
하지만 이 방법들 역시 완벽한 해결책은 아닙니다.
레플리카는 동기화 비용이 발생하고, 샤딩은 쿼리 구조와 운영 복잡도를 급격히 증가시킵니다.
인덱스를 추가하면 읽기 성능은 개선될 수 있지만, 이 역시 물리적인 한계가 존재합니다.
결국 DB는 상태(State)를 가진 시스템이며, 이 특성 때문에 확장성에는 명확한 한계가 있습니다.
어떻게 보면 DB는 정확함을 책임지는 시스템이지, 대량의 읽기 요청을 빠르게 분산 처리하는 시스템은 아닙니다.
여기서 중요한 질문이 하나 생깁니다.
모든 데이터를 항상 DB에서 읽어야 할까요?
조금 오래된 값이라도 괜찮은 데이터는 없을까요?
이 질문에서 나온 선택이 바로 캐시입니다.
DB 앞단에 메모리 기반으로 자주 조회되는 데이터를 복제해 두고, DB를 거치지 않고 바로 응답하는 방식입니다.
즉, 정확함을 조금 희생하는 대신 속도와 확장성을 얻는 선택을 하게 된 것이고, 이것이 캐시의 시작입니다.

그렇다면, 모든 데이터를 캐시로 만들어도 되는 걸까요? 위에서 말했듯이 모든 데이터는 그렇지는 않습니다.
어떤 기준으로 캐시로 사용할 수 있는 데이터로 구분하는걸까요?
이 조건들은 판단 기준이지 절대 기준은 아닙니다.
1. 조금 오래돼도 괜찮은 데이터인가?
캐시는 최신 값을 항상 보장하지 않습니다. 따라서 캐시 대상 데이터는 짧은 시간 동안 값이 어긋나도 서비스에 치명적이지 않아야 합니다.
예를 들면, 조회수 좋아요 수 인기 랭킹 피드 목록 같은 것들이죠.
반대로 결제 상태, 재고 수량 같은 데이터는 캐시 대상이 되기는 어렵죠.
2. 읽기 비중이 높은 데이터인가?
캐시는 읽기 부하를 줄이기 위한 계층입니다. 즉, 읽기 빈도가 높고, 쓰기 빈도가 상대적으로 낮은 데이터일 수록 효과가 큽니다.
설정 정보, 상품 정보, 카테고리 목록 같은것들이죠. 이것들 처럼 자주 읽히고, 가끔 바뀌는 데이터는 캐시와 궁합이 좋습니다.
3. 같은 데이터가 반복해서 조회하는가?
캐시는 중복된 읽기를 흡수하기 위해 존재합니다. 매 요청마다 같은 값을 조회하는 경우 또는 여러 사용자가 동일한 데이터를 조회하는 경우등등 이런 비슷한 패턴들은 캐시를 이용해서 불필요한 복잡도를 줄일 수 있죠.
4. 정합성의 책임이 어디에 있는가?
캐시는 Source of Truth가 아닙니다. 정합성의 최종 책임은 항상 DB에 있어야 합니다.
즉, 캐시가 틀려도, DB 기준으로 다시 복구될 수 있어야 합니다. 이 조건이 성립하지 않으면 캐시로 두면 안됩니다.
결국 캐시는 조금 틀려도 괜찮고, 자주 읽히며, 반복 조회되고, 최종 책임이 DB에 있는 데이터에만 사용해야 합니다.
그렇다면 쓰기 전략은 어떻게 가져가는게 좋을까?
캐시는 읽기 성능을 올리기 위해 사용되는 기술 중 하나입니다.
하지만 캐시를 사용한다는 건, 결국 쓰기 없이 읽기만 할 수는 없다는 의미이기도 합니다.
그래서 캐시를 사용할 때는, 어떻게 읽을지뿐만 아니라 어떻게 쓸지도 굉장히 중요해집니다.
아래 3가지의 캐시 전략을 소개하고 그에 따른 트레이드 오프도 함께 살펴보도록 하겠습니다.
1. Write-Through Caching
쓰기를 통과해서 캐싱을 한다?
여기서 말하는 "통과한다"는 건 무엇을 의미할까요?
앞에서 말했듯이, 캐시는 데이터의 기준이 될 수 없습니다.
데이터의 기준, 즉 데이터의 기준은 캐시가 아니라 데이터베이스가 되어야 합니다.
그래서 Write-Through Caching에서 "쓰기를 통과해서 캐싱을 한다"는 말은,
쓰기 요청이 DB를 기준으로 끝까지 관통한다는 의미로 볼 수 있습니다.

쓰기 요청은 캐시를 거쳐서, 반드시 DB까지 이어집니다. 그리고 DB가 쓰기에 대해 성공 응답을 서버에 전달했을 때, 그제서야 쓰기는 성공으로 간주됩니다. 만약 캐시에만 쓰기가 성공하고 DB에는 반영되지 않았다면, 이건 성공이 아닙니다.
이 흐름에서는 캐시의 데이터가 DB의 데이터와 항상 동일해야 합니다. 즉, 캐시는 DB에 반영된 결과를 그대로 따라가는 구조가 되고,
그 결과 캐시 역시 항상 최신 값을 유지할 수 있게 됩니다.
이런 접근 방식은 캐시의 정확도가 무엇보다 중요한 시스템이나, 쓰기 요청은 많지 않지만 읽기 요청이 많은 시스템에서 잘 동작합니다.
개인적으로는 데이터의 크기는 크지만 자주 변하지 않고, 무엇보다 항상 최신 값을 보여줘야 하는 경우에 적합한 방식이라고 생각합니다.
Write-Through 방식은 캐시와 DB가 거의 한 몸처럼 움직이기 때문에, 데이터에 대한 신뢰도가 높다는 장점을 가집니다.
다만 쓰기 작업이 캐시와 DB에 이중으로 발생하기 때문에, 쓰기 부하가 커질 수 있다는 잠재적인 위험도 함께 가지고 있습니다.
2. Cache Aside Approach
직역하자면, Cache Aside는 캐시를 옆에 두고 작업하는 방식이라고 볼 수 있습니다.
이 말은 곧, 캐시는 잠시 옆으로 치워두고 다른 무언가가 중심이 되어 작업이 진행된다는 뜻이 됩니다.
그 중심이 되는 것은 무엇일까요? 바로 데이터베이스입니다.
Write-Through Caching과 달리, 이 방식에서는 쓰기 작업을 먼저 DB에만 수행합니다.
즉, 쓰기 시점에는 캐시는 전혀 관여하지 않습니다.
그렇다면 캐시에는 언제 데이터가 저장될까요? 바로 데이터를 읽는 시점입니다.
읽기 요청이 들어왔을 때 캐시에 데이터가 없다면(DB 기준으로 조회한 뒤), 그 결과를 캐시에 저장하는 방식입니다.

이 방식의 특징은, 초기 쓰기 과정에서 캐시를 전혀 건드리지 않는다는 점입니다. 어떻게 보면 캐시는 사용자가 실제로 데이터를 요청했을 때만 사용되기 때문에, 상황에 따라서는 더 효율적인 방식이라고 볼 수도 있습니다.
다만 이 방식은 캐시 미스가 발생할 수 있다는 전제를 가지고 가야 합니다.
특히 최초로 데이터를 읽는 경우에는 캐시를 사용할 수 없기 때문에, 읽기 성능이 기대만큼 좋지 않을 수 있습니다.
결국 Cache Aside 방식을 선택한다는 것은, 캐시 미스를 감수하겠다는 선택에 가깝습니다.
그래서 이 방식은 쓰기 요청은 많지만, 쓰기 이후에 곧바로 읽힐 가능성이 낮은 경우에 주로 사용됩니다.
3. Write-Back Caching
이 방식은 데이터베이스와 캐시의 주종 관계를 뒤집는 방식이라고 볼 수 있습니다.
즉, 캐시가 주가 되고 데이터베이스가 부가적인 역할을 하게 됩니다.
겉으로 보면 1번 Write-Through 방식과 유사해 보일 수 있지만, 실제로는 전혀 다른 접근입니다.
이 방식에서는 최초로 쓰기 작업이 발생했을 때 캐시에만 데이터를 적재합니다.
데이터베이스에는 즉시 반영하지 않고, 이후 스케줄링이나 배치 같은 별도의 프로세스를 통해 데이터를 저장합니다.

이 구조에서 데이터베이스의 역할은 실시간 처리를 담당한다기보다는 기록을 남기는 용도에 가깝다고 볼 수 있습니다.
이 방식의 가장 큰 장점은 속도입니다. 쓰기 요청이 데이터베이스를 기다릴 필요 없이 캐시에서 바로 처리되기 때문에,
쓰기 성능과 처리량 면에서는 매우 유리한 구조입니다.
다만 앞에서 말했듯이, 캐시는 정합성이 강한 계층이 아닙니다.
이는 곧 캐시에 저장된 데이터를 항상 신뢰할 수는 없다는 의미이기도 합니다. 그래서 이 방식을 선택한다는 것은,
일부 데이터가 손실되더라도 서비스 이용에는 문제가 없어야 한다는 전제를 함께 가져가야 합니다.
하지만 이 방식들은 단일 환경의 경우입니다. 그렇다면 분산 환경에서는 어떨까요?
분산환경에서 캐시는 어떻게???
단일 환경이라면, 앞에서 이야기한 방식들로 어느 정도는 커버가 가능해 보입니다.
하지만 환경이 분산된다면 이야기는 달라집니다. 과연 이런 방식들이 분산 환경에서도 그대로 동작할 수 있을까요?
개인적으로는, 쉽지 않다고 생각합니다.
여러 클라이언트가 동시에 접근하거나, 더 나아가 서로 다른 데이터 센터에 있는 여러 서비스가
동일한 데이터를 읽고 쓰는 상황을 생각해보면, 캐시가 항상 동일한 상태를 유지한다고 기대하기는 어렵습니다.
이런 환경에서는 캐시가 서로 다른 시점의 데이터를 가지고 있을 수 있고,
그 결과 사용자는 오래된 데이터를 보게 됩니다. 또한 동시에 쓰기 요청이 들어오면서 race condition이 발생할 수 있고,
문제는 이 상황들이 재현도 어렵고 원인 파악도 쉽지 않다는 점입니다.
결국 분산 캐시 환경에서는 정합성 문제를 완전히 제거하는 것은 거의 불가능하고,
디버깅 비용 역시 크게 증가하게 됩니다.
어떤 문제가 있는지 살펴보겠습니다.
캐시 불일치
분산캐시는 여러가지 이유로 동기화가 되지 않습니다.
오래된 데이터 조회 (Stale Read)
분산 환경에서는 캐시의 접근 순서를 보장할 수 없습니다. 어떤 쓰기 요청이 특정 캐시 노드에 반영되었다고 해서,
그 이후의 읽기 요청이 반드시 같은 캐시 노드로 향한다는 보장은 없습니다.
이로 인해, 이미 갱신이 완료된 캐시가 존재함에도 불구하고 클라이언트가 이전에 쓰기가 반영되었던 다른 캐시 노드에서
데이터를 읽게 되는 상황이 발생할 수 있습니다.
이런 상황을 오래된 데이터 조회(Stale Read) 라고 합니다. 같은 데이터를 조회했는데도,
요청 시점이나 경로에 따라 서로 다른 값이 보이게 되는 문제입니다.
이 문제가 반복되면 데이터의 불일치가 누적될 수 있고,
시스템에 따라서는 실제로 데이터가 유실된 것처럼 보이는 상황까지 이어질 수 있습니다.

동시 업데이트(Race Condition)
분산 환경에서는 쓰기 요청이 어떤 캐시 노드에 반영될지를 확정할 수 없습니다. 즉, 하나의 캐시에만 쓰기 요청이 들어가는 것이 아니라,
여러 캐시 노드에 동시에 쓰기 작업이 발생할 수 있는 상황을 의미합니다.
이렇게 여러 노드에서 동일한 키에 대해 동시에 쓰기가 발생하면, 캐시에는 결국 하나의 값만 남게 됩니다.
하지만 그 값이 가장 최신의 데이터인지, 혹은 올바른 순서로 반영된 결과인지는 확신할 수 없습니다.
이로 인해 쓰기 순서가 뒤섞이거나, 의도하지 않은 값이 최종 상태로 남는 문제가 발생할 수 있습니다.

부분 무효화(Partial Invalidation)
데이터베이스에는 정상적으로 데이터 업데이트가 이루어졌지만,
어떠한 이유로 인해 캐시 무효화가 모든 노드에 전파되지 않았다고 가정해봅시다.
이 경우 일부 캐시는 여전히 이전 데이터를 유지하게 되고,
그 결과 클라이언트는 최신 데이터가 아닌 과거의 데이터를 읽는 상황에 놓일 수 있습니다.

이 밖에도 네트워크 지연이나 장애로 인한 문제가 발생할 수 있습니다.
앞에서 말했듯이 캐시는 정합성이 절대적으로 중요한 데이터를 다루기 위한 계층은 아닙니다.
그렇기 때문에 캐시를 사용하는 경우에는, 일부 데이터가 지연되거나 일시적으로 소실되더라도
서비스 전체에 치명적인 영향을 주지 않는다는 전제가 필요합니다.
즉, 조금 어긋나도 감당할 수 있는 데이터라면 네트워크 지연이나 일시적인 불일치가 발생하더라도
큰 문제로 이어지지 않는다고 판단하고 캐시를 사용하는 것입니다.
결론
캐시는 단순히 성능을 올리기 위해 무작정 사용하는 기술은 아니라고 생각합니다. 사용하는 순간부터 정합성, 데이터 유실, 분산 환경에서의 문제까지 여러 가지를 함께 고려해야 하는 선택이 됩니다. 그래서 캐시는 사용해도 괜찮은 경우, 혹은 사용하지 않아도 큰 문제가 없는 데이터에 한해 신중하게 도입되어야 한다고 생각합니다. 이번 글에서는 캐시의 대표적인 쓰기 전략인 Write-Through, Cache Aside, Write-Back을 살펴보면서 각 전략이 가지는 트레이드오프와 어떤 상황에서 선택되는지를 정리해보았습니다. 또한 단일 환경을 넘어 분산 환경에서 캐시를 사용할 경우 발생할 수 있는 정합성 문제와 그 한계에 대해서도 함께 살펴보았습니다. 결국 캐시를 잘 사용한다는 것은 캐시를 얼마나 적극적으로 쓰느냐가 아니라, 어디까지 어긋나도 괜찮은지를 명확히 정의하는 것에서 시작된다고 생각합니다.
출처
https://blog.bytebytego.com/p/a-guide-to-top-caching-strategies?utm_source=publication-search
'개발' 카테고리의 다른 글
| Lint는 무엇인가? (1) | 2026.01.08 |
|---|---|
| MDC란 무엇인가 그리고 어떻게 설정할 수 있을까? (1) | 2026.01.06 |
| WAL: 분산 시스템에서 쓰기 경로를 중앙화하는 방법 (0) | 2026.01.03 |
| 멀티 모듈 개발기(1) (feat.grale) (0) | 2026.01.02 |
| 스쿼시, 머지, 리베이스 (0) | 2025.12.31 |