그래서 캐시는 왜 쓰는 건데요?

SUNGKYUM KIM·2024년 11월 8일
2
post-thumbnail

어쩌다 이런 세상이 왔는지는 모르겠지만 레디스가 신입의 기본이라는 이야기가 떠도는 시대가 되었습니다. 저와 같은 초보 개발자들에게는 취업의 문턱이 점점 높아져만 가는 것 같아서 두렵기도 합니다. 다만 참된 개발자라면 기술에 집착하기 보다는 기술의 본질, 해결하고자 하는 문제와 장단점을 깊게 공부하는 자세가 필요하겠죠. 이번 글에서는 코드레벨에서의 캐시 구현 방법보다는 캐시 사용에 있어서 어떠한 고민들이 필요한지 나누어보겠습니다.

Redis와 첫 만남

저는 우아한테크코스의 쿠폰 시스템 구현 미션에서 처음 레디스 캐시를 접했습니다. 임의로 n초의 복제 지연 상황을 구현해놓은 Reader/Writer 용 복제 데이터베이스 환경이었죠. 조회 연산이 발생할 때 복제 지연으로 인해 Reader 용 데이터베이스에 데이터가 존재하지 않는 상황을 어떻게 해결할 것 인지 학습하는 것이 목적이었습니다. 이를 테면 다음과 같은 상황입니다.

@Test
void 복제지연테스트() {
		/**
		@Transactional의 readOnly 옵션의 true/false 옵션에 따라 
		Reader/Writer 용 DataSource를 참조하도록 구현되어있음
		*/ 
    // given
    Coupon coupon = new Coupon("테스트 쿠폰", 1000);
    couponService.create(coupon); // Writer DB에 쿠폰 저장

    // when
    Coupon savedCoupon = couponService.getCoupon(coupon.getId()); // Reader DB 조회

    // then
    assertThat(savedCoupon).isNotNull(); // 복제 지연으로 인해 바로 조회가 불가능
}

가장 간단한 해결책은 다음 두개가 떠올랐어요.

  1. 서비스 조회 코드(getCoupon)에 Thread.sleep(xxx) 를 건다.
  2. 테스트 코드에 Thread.sleep(xxx) 을 건다.

네, 이상하죠. 서비스 코드를 수정하여 모든 사용자에게 임의로 조회 성능을 떨어뜨리는 건 상식적이지 않습니다. 또한 테스트를 수정하더라도 테스트만 통과하는 코드가 되어버리겠죠. 무엇보다 문제는 실제 환경에서 복제 지연이 얼마나 걸릴 지는 예상하기 어렵다는 사실입니다.

그럼 어떻게 해야할까요?

가장 간단한 방법은 조회 시에는 Writer 데이터베이스에서 읽는 방법이 있습니다. 타당한 방법이지만 코드 구현에 따라 다른 개발자들이 조회를 Writer 데이터베이스에서 읽는다는 사실을 이해하기 어려울 수 있을거라 생각했습니다. 그래서 저는 캐시를 선택해보았습니다.

private final RedisTemplate<String, Coupon> redisTemplate; // 캐시로 레디스를 사용

@Transactional
public Coupon create(Coupon coupon) {
    Coupon savedCoupon = couponRepository.save(coupon);
    redisTemplate.opsForValue().set("coupon:" + coupon.getId(), savedCoupon); // 쿠폰 저장시 캐시도 같이 저장
    return savedCoupon;
}

@Transactional(readOnly = true)
public Coupon getCoupon(long id) {
		// 캐시를 먼저 조회
    Coupon cachedCoupon = redisTemplate.opsForValue().get("coupon:" + id);
    if (cachedCoupon != null) {
        return cachedCoupon;
    }
    Coupon coupon = couponRepository.findById(id)
        .orElseThrow(() -> new IllegalArgumentException("id에 해당하는 쿠폰이 존재하지 않습니다."));
    redisTemplate.opsForValue().set("coupon:" + id, coupon);
    return coupon;
}

데이터베이스에 저장할 때 캐시도 함께 저장하고 조회 시 캐시를 먼저 조회하는 방법입니다. 깔끔하게 해결했네요!

그런데…정말일까요..?

아..캐시 그렇게 쓰는거 아닌데…

저는 복제 지연을 해결하기 위한 해결책으로 캐시를 사용했습니다. 그런데 몇가지 의문이 듭니다.

  1. 캐시를 사용하면 정말 문제가 해결되는가?
  2. 캐시가 해결하고자 하는 문제가 정말 복제 지연을 해결하는 걸까?

하나씩 생각해봅시다.

캐시를 사용하면 정말 복제 지연으로 인한 문제가 해결되는가?

물론 아닙니다. 컴퓨터 세상의 문제는 호락호락하지 않죠. 캐시 레이어를 도입하게 되면 복제 지연 이외의 또 다른 문제들을 고민해야 합니다. 여러 문제 중 한 가지인 동시성 문제를 생각해봅시다.

먼저 서로 다른 두 서버에서 쿠폰을 생성하는 시나리오입니다. 위 상황 처럼 데이터베이스 저장 로직과 캐시 저장 로직 사이에 다른 생성 요청이 들어온다 하더라도 큰 문제가 발생하지 않습니다.

하지만 두 서버에서 동시에 쿠폰을 업데이트 한다면 어떨까요? 데이터베이스 업데이트와 캐시 업데이트 작업 사이 발생한 다른 요청이 먼저 완료된다면 위처럼 데이터베이스와 캐시 저장소의 데이터 불일치가 발생합니다. 이로 인해 캐시레이어를 먼저 접근하는 지금의 로직에서는 정합성이 지켜지지 않은 데이터를 계속해서 조회하게 됩니다.

그렇다면 업데이트 쿼리의 데이터를 저장하지 말고 데이터베이스에 반영된 데이터를 읽어서 캐시를 저장하면 어떨까요? 중간에 다른 요청이 먼저 처리되더라도 데이터베이스의 최종본을 읽게 될테니 캐시 정합성에는 문제가 없어보입니다.

하지만 업데이트 된 데이터를 조회한 후 캐시 업데이트 사이에 다른 요청이 처리된다면 여전히 데이터 불일치 문제가 발생하고 맙니다.

캐시가 해결하고자 하는 문제가 정말 복제 지연을 해결하는 걸까?

캐시의 본래 목적은 데이터 접근의 속도 향상입니다. 즉, 데이터베이스에 직접 접근하는 비용이 큰 경우, 자주 접근하는 데이터를 메모리에 저장함으로써 전체적인 응답 속도를 빠르게 하기 위한 수단이죠. 반면, 복제 지연 문제는 데이터 일관성과 관련된 문제입니다. 주로 데이터베이스가 여러 개의 인스턴스로 분리되어 있을 때, 데이터 쓰기와 읽기 간의 시간 차이로 인해 발생하는 데이터 불일치 상황을 지칭합니다.

이 둘의 목적이 다르다 보니, 캐시를 사용하여 복제 지연 문제를 해결하려고 할 때 캐시 본래의 특성으로 인해 발생하는 새로운 문제가 생길 수 있습니다. 캐시는 최신 데이터를 보장하지 못할 수 있기 때문입니다. 특히 데이터베이스와 캐시 간의 동기화 문제가 발생할 때, 복제 지연이 문제가 되는 시나리오에서 캐시가 오히려 데이터를 더 혼란스럽게 만들 가능성이 높습니다.

캐시의 유효성을 유지하기 위해 TTL(Time To Live) 을 설정하거나, 캐시 무효화 정책을 적용하는 등의 방법을 사용할 수 있지만, 이 역시 완벽한 해결책은 아닙니다. 복제 지연의 원천적인 해결은 읽기 작업을 일관성 있게 수행할 수 있는 구조를 만드는 것이며, 이를 위해 복제된 데이터베이스의 구조를 개선하거나, 특정 상황에서는 쓰기 데이터베이스를 직접 참조하게 하는 등의 방법이 더 적절할 수 있습니다.

결국 캐시는 접근 속도를 높이기 위한 수단일 뿐, 일관성을 보장하는 수단은 아니기 때문에 복제 지연 문제를 해결하기 위한 최선의 방법이라고 볼 수 없습니다. 복제 지연 문제를 해결하려면, 데이터베이스의 복제 아키텍처를 조정하거나, 일관성을 보장할 수 있는 데이터 접근 전략을 선택하는 것이 바람직합니다.

캐시를 사용할 때 고민해봐야 하는 부분들

캐시는 분명히 데이터 접근 속도를 높이는 데 유용한 도구이지만, 이를 사용할 때에는 여러 가지 고려사항이 있습니다. 캐시의 특성상, 제대로 사용하지 않으면 오히려 시스템의 일관성과 안정성에 부정적인 영향을 미칠 수 있겠죠. 캐시를 사용할 때 고민해봐야 하는 주요 지점들은 다음과 같습니다.

언제 캐시를 사용해야 할까?

캐시는 모든 상황에서 사용하는 것이 아니라, 데이터 접근 빈도가 높고 데이터 변경 빈도가 낮은 경우에 가장 효과적입니다. 예를 들어, 자주 조회되지만 거의 변경되지 않는 설정 정보나 공통 코드 데이터 등을 캐시에 적재하면 응답 속도를 크게 향상시킬 수 있습니다. 반면에 데이터 변경이 잦다면 캐시와 데이터베이스 간의 일관성 유지 비용이 커지므로 캐시 사용이 오히려 역효과를 낼 수도 있겠죠.

또한 읽기 성능이 중요한 서비스에서 캐시는 큰 도움이 됩니다. 특히 외부 API 호출처럼 응답 시간이 긴 작업의 결과를 캐시에 저장하여 반복 호출을 줄이는 것이 효과적입니다. 이를테면 자주 변하지 않는 날씨 데이터를 가져오는 API 같은 상황이 있겠네요. 그러나 데이터의 최신성이 중요한 경우에는 캐시 사용을 신중히 고려해야 합니다. 최신 데이터가 반드시 보장되어야 하는 금융 거래나 실시간 통계 같은 경우에는 캐시의 부정확성이 큰 문제가 될 수 있습니다.

어떤 데이터를 적재해야 할까?

캐시에 적재할 데이터를 선택할 때는 캐시 적중률(Cache Hit Ratio)을 고려해야 합니다. 자주 조회되는 데이터일수록 캐시에 적재하는 것이 효과적입니다. 이를 위해 시스템의 데이터 접근 패턴을 분석하는 것이 중요합니다. 캐시에 적재하는 데이터가 자주 조회되지 않으면, 캐시 적중률이 낮아지고 캐시의 이점이 줄어들게 됩니다.

대용량 데이터의 경우에는 캐시에 적재할 때 주의해야 합니다. 캐시는 기본적으로 메모리를 사용하는 구조이기 때문에, 너무 큰 데이터를 캐시에 저장하면 메모리 사용량이 급격히 증가하여 시스템 성능에 영향을 미칠 수 있습니다. 따라서 캐시에는 자주 사용되지만 크기가 작은 데이터를 저장하는 것이 좋습니다.

또한 중복 데이터 저장을 피하고, 필요한 데이터를 효율적으로 캐싱하기 위해 적절한 키 설계가 필요합니다. 예를 들어, 캐시 키를 정의할 때는 데이터를 쉽게 찾을 수 있도록 하고, 충돌을 방지하기 위해 유니크한 키를 사용하는 것이 중요합니다.

캐시 일관성은 어떻게 유지해야 할까?

캐시와 데이터베이스 간의 일관성을 유지하는 것은 캐시 사용의 가장 큰 도전 과제 중 하나입니다. 일관성을 유지하기 위해 고려할 수 있는 몇 가지 방법이 있습니다.

  1. 캐시 무효화(Cache Invalidation)
    • 데이터베이스의 데이터가 변경될 때 해당 데이터를 캐시에서 제거하거나 업데이트해야 합니다. 이를 캐시 무효화라고 하며, 데이터의 변경 시점과 캐시의 상태를 동기화하는 것이 핵심입니다. 무효화가 적시에 이루어지지 않으면 오래된 데이터를 참조하게 되는 문제가 발생할 수 있습니다.
  2. Write-through, Write-behind, Write-around
    • Write-through 방식은 데이터베이스에 데이터를 쓰는 동시에 캐시에도 데이터를 저장하는 방식입니다. 이 방식은 일관성은 높일 수 있지만 쓰기 작업의 성능이 저하될 수 있습니다.
    • Write-behind 방식은 캐시에 먼저 데이터를 저장하고 일정 시간 후에 비동기적으로 데이터베이스에 저장하는 방식입니다. 쓰기 성능은 높지만, 데이터베이스와 캐시 간의 일관성 문제가 발생할 수 있습니다.
    • Write-around 방식은 데이터베이스에만 쓰고 캐시는 무효화하는 방식입니다. 데이터의 최신성을 보장하지만, 첫 조회 시 캐시 적중률이 낮아지는 문제가 있을 수 있습니다.
  3. 분산 잠금(Locking)
    • 여러 서버에서 동시에 데이터를 업데이트할 때 캐시와 데이터베이스 간의 불일치를 방지하기 위해 분산 잠금을 사용할 수 있습니다. 이를 통해 데이터가 동시에 변경되는 상황을 제어하고, 일관성을 유지할 수 있습니다. 그러나 분산 잠금을 사용할 경우 성능이 저하될 수 있으므로 필요한 경우에만 신중히 적용해야 합니다.
  4. TTL(Time To Live)와 캐시 재생성
    • 캐시의 유효 기간을 설정하여 TTL(Time To Live)을 활용하는 것도 일관성을 유지하는 방법 중 하나입니다. TTL을 설정하면 일정 시간이 지난 후 캐시 데이터를 자동으로 무효화하고, 다음 조회 시 최신 데이터를 데이터베이스에서 가져오게 됩니다. 이 방법은 일관성을 어느 정도 보장할 수 있지만, TTL이 지나기 전까지는 오래된 데이터를 참조할 수 있는 위험이 있습니다.

캐시 일관성을 유지하기 위한 전략은 시스템의 특성과 요구사항에 따라 달라져야 합니다. 캐시의 일관성을 엄격히 유지해야 하는 경우에는 캐시 사용 자체를 지양하거나, 일관성 유지에 최적화된 캐시 아키텍처를 설계하는 것이 필요합니다.

No Silver Bullet

네 맞습니다. 소프트웨어 세상의 유일한 진리는 오직 은총알은 없다는 사실 뿐이죠. 캐시 또한 마찬가지 입니다. 캐시가 가진 여러 특성들 때문에 서비스 도메인 특성에 따른 다양한 전략이 필요합니다. 그럼 업계에서는 어떻게 캐시를 활용했고 어떤 점을 고려했는지 실제 사례를 통해 배워보도록 해봅시다.

인프런의 사례

먼저 현 국내 최대 규모의 온라인 교육 플랫폼인 인프런 사례를 살펴봅시다. 인프런은 교육 도메인을 웹, 모바일 앱으로 사용자들에게 제공하는 B2C 서비스라는 특징이 있습니다. 그리고 최근 인프런 아키텍처 2024 ~ 2025 라는 인프콘 발표를 통해서 캐시를 활용한 사례를 공유해 주었습니다. 여러 주제가 있었지만 이번에는 불필요한 카테고리 반복 조회 문제를 Json CDN 캐시로 해결한 사례에 집중해보겠습니다.

교육 서비스인 만큼 카테고리는 사용자가 적절한 강의를 찾기 위해 매우 중요합니다. 인프런도 카테고리를 탭의 형태로 제공하고 있습니다. 그리고 거의 모든 페이지에서 카테고리를 조회할 수 있습니다. 이는 인프런 서비스 트래픽에서 카테고리 조회 API 호출만 고려하여도 어마어마한 양이라는 걸 예상하게 합니다.

그리고 실제로 매일 약 150GB(!)의 json 데이터가 카테고리 조회만을 위해 트래픽을 점유했다고 합니다. 물론 데이터베이스 부하는 덤이구요. 이 문제를 인프런에서는 어떻게 해결했을까요?

처음 인프런에서는 AWS가 제공하는 외부 캐시인 Elastic Cache 를 고려했으나 어마어마한 트래픽을 외부 캐시가 감당하게 했을 때 비용이 데이터베이스 스펙을 올리는 것과 큰 차이가 없었기에 로컬 캐시를 고려해봤다고 합니다. 그럼 데이터베이스로 가는 수많은 트래픽을 아낄 수 있고 자연스레 비용 절감도 누릴 수 있습니다.

그러나 로컬 캐시는 EC2로 향하는 트래픽을 개선해 줄 수는 없습니다. 캐시 조회는 결국 어플리케이션이 담당하기 때문입니다. 로컬 캐시가 트래픽 부하 및 과도한 금액 문제의 완벽한 해결책이 아니었던 것 이죠.

그래서 인프런은 Json 데이터를 로컬 캐시가 아닌 CDN 캐시를 사용하기로 결정합니다. 결국 json도 하나의 파일이며 카테고리 정보는 쉽게 변경되지 않는 비교적 정적인 데이터라는 특징이 있기 때문이죠.

이를 통해 인프런은 무려 애플리케이션 로드 70% 감소 및 로드밸런서 트래픽 50% 감소라는 엄청난 트래픽 절감할 수 있었습니다. 일 150GB, 월 4.5TB의 데이터를 EC2가 처리할 필요가 없어지게 된 것 입니다. 로컬 캐시가 언제가 꼭 정답은 아니며 데이터의 특성에 따라 다양한 종류의 캐싱을 고려해야 한다는 좋은 사례라고 할 수 있습니다.

핀다의 사례

두번째는 핀테크 스타트업, 핀다의 사례입니다. 핀다는 블로그 글을 통해 금융 서비스를 다루는 실무에 레디스 캐시를 적용한 사례를 공유해주었습니다. 이 중에서 주목할 부분은 MYDATA Refresh API 를 구현한 내용입니다.

핀다는 MYDATA Refresh API 를 구현할 때 캐싱 전략 중 하나인 Write-Back(Write-Behind 라고도 부릅니다)를 선택했습니다.

Write-Back 패턴이란?
데이터 쓰기 작업 시 이를 먼저 캐시에 반영한 후 사용자에게 반환합니다. 이후 일정 기간 동안 쌓인 캐시의 데이터를 별도의 비동기 작업(배치 등)을 통해 캐시에 저장합니다. 캐시 레이어를 일종의 쓰기 버퍼로 사용하는 캐시 전략입니다. 데이터 쓰기 작업의 부하를 분산할 수 있고, 일정 시간 텀이 있지만 데이터 정합성을 지속적으로 유지할 수 있다는 장점이 있는 반면, 캐시레이어에 장애가 발생하면 데이터가 영구히 소실될 수 있다는 단점이 존재합니다.

얼핏 Write-Back의 데이터 영구 손실 가능성이 있는 단점과, 실시간 데이터를 보장할 수 없는 단점으로 인해 금융 서비스와는 어울리지 않는다는 생각이 듭니다. 다만 핀다에서 해당 패턴을 적용한 데이터는 핀다의 사용자가 만들어낸 데이터가 아닌 외부에서 조회하는 금융 데이터라는 특징을 가지고 있습니다. 핀다가 조회한 외부데이터가 캐시 레이어의 문제로 인해 소실된다하더라도 원본 데이터는 여전히 외부 포탈이 잘 소유하고 있기에 문제 될 것이 없습니다. 다만 데이터를 얼마나 빠르게 전달할 것인가, 이 과정 중에 발생할 수 있는 데이터베이스 부하를 어떻게 개선할 것인가가 더 우선되는 문제입니다.

핀다 서버는 먼저 외부 API인 MYDATA Portal 에서 조회한 정보를 빠르게 사용자에게 전달합니다. 이후 이 데이터를 데이터베이스에 바로 접근하여 업데이트하는 것이 아니라 별도의 Queue에 저장합니다. 그리고 일정시간마다(핀다는 100ms) 지정된 배치 사이즈 만큼 데이터베이스에 저장합니다. 비교적 Write 가 자주 일어나는 MYDATA를 그때마다 데이터베이스에 적용시켜줄 필요가 없어지게 되는 것이죠. 이로써 정보는 빠르게 전달하고 데이터베이스 부하는 줄일 수 있는 효과를 누립니다. 이와 비슷한 사례를 우리는 실제로 자주 접할 수 있습니다.

위 사진은 제 토스 자산 정보 화면을 캡쳐한 화면입니다. 화면에는 130만원 가량이 통장에 존재한다는 정보를 출력하고 있습니다.

하지만 해당 통장을 직접 조회하면 분명 같은 시각임에도 39만원의 금액으로 출력하고 있는 것을 볼 수 있습니다. 실제로는 입출금 내역이 더 많이 존재하고 있지만 반영되지 않은 것이죠. 또한 아래의 토스트 메시지에서 내역을 업데이트하는 중이라 일시적으로 잔액이 늦게 반영될 수 있다는 정보를 확인할 수 있습니다. 토스도 마찬가지로 MYDATA를 활용한 금융데이터를 제공하는 서비스이기에 비슷한 전략으로 구현하였을 것이라는 추측을 해볼 수 있습니다.

여기서 생각해볼 지점은 사용자의 통장 이용 내역은 언제나 최신정보를 유지해야 할까 입니다. 그리고 핀다와 토스 모두 그럴 필요는 없다는 결론을 내린 것이죠. 사용자가 금융 데이터가 빠르게 최신화 되지 않는 현상을 발견하는 것, 일시적 불일치가 치명적이지 않다는 판단이라고 할 수 있습니다. 사용자들이 금융데이터 조회 서비스에게 기대하는 것은 실시간 데이터 보다는 정확한 정보를 조회하는 것에 가까울 수 있기 때문입니다. 실제로 저 또한 금액이 바로 반영되지 않은 것에 약간의 당황스러움은 느꼈지만 몇번의 새로고침을 통해 무사히 최신화된 통장 잔액을 조회할 수 있었습니다.

이는 서비스 도메인과 데이터 특성에 따라 캐싱 전략을 적절히 선택해야 한다는 교훈을 주는 사례입니다.

우아한형제들의 사례

마지막으로 우아한형제들의 프론트검색시스템 팀의 사례입니다. 프론트검색시스템 팀은 배달의 민족 앱에서 사용자에게 가게를 보여주는 가게노출 시스템 개발기를 공유해주었습니다. 블로그 글을 읽으며 배달의 민족이 가게 목록을 보여주기까지 과정이 생각보다도 훨씬 복잡한 것에 놀랐는데요. 해당 글에서 저장소 아키텍처를 설계하며 캐시를 활용한 사례에 집중해보겠습니다.

가게노출 시스템은 특이하게도 무려 3개의 저장소를 사용하고 있습니다. 이는 다양한 요구사항에 대응하기 위한 구조를 고민한 결과라고 하는데요. 우리는 각 저장소들의 역할과 각 저장소들을 관리하는 정책에 주목할 필요가 있습니다.

3차 저장소인 다이나모 데이터베이스는 원본 형태의 데이터를 저장하는 주 저장소로 데이터 조회 및 Fallback 동작의 종단으로 사용하고 있다고 합니다. 즉 앞의 두 캐시 저장소 데이터의 근간이자 앞의 두 캐시 조회가 모두 실패할 경우 도달하게 되는 최종 데이터 조회 지점이라고 할 수 있습니다.

Fallback 이란?
어떤 기능이 약해지거나 제대로 동작하지 않을 때, 이에 대처하는 기능 또는 동작

2차 저장소인 캐시 레이어는 3차 저장소의 캐시 역할을 하며 정밀한 고속 데이터에 대한 처리를 위해 사용하고 있다고 합니다. 마지막으로 1차 저장소는 실제 가게노출을 위해 재구성된 데이터입니다.

재미있는 점은 2,3차 저장소의 형태와 1차 저장소의 형태가 다르다는 것 입니다. 이는 가게노출 시스템이 다루는 데이터의 특성 때문입니다.

가게노출 시스템은 수많은 마이크로 시스템들의 집합인 배달의 민족 서비스 중에서도 최전방에 위치하고 있습니다. 이는 곧 가게노출 데이터를 전송하기 위해서는 수많은 외부 서비스의 데이터에 의존해야 한다는 것을 의미합니다. 2021년 발표에 따르면 1개의 가게를 노출하는데 필요한 외부 요청 수가 무려 13개라고 합니다. 여기에 시간 + 장소 정보까지 적절히 결합된 형태가 우리가 접하는 실제 가게노출 데이터(가게 이름, 메뉴, 적용 가능 쿠폰, 배달 팁 등등)인 것 입니다.

이때 가게에 필요한 데이터들을 각각 분산된 형태로 전달할 수는 없습니다. 정확한 가게 정보 노출을 위해 가공하는 과정이 필요합니다. 이를 테면 가게와 업주 시스템은 서로 분리되어 있지만 실제 데이터를 전송할 때는 하나의 데이터로 사용되기 때문에 두 데이터를 합쳐 재구성한 데이터를 만들어냅니다. 바로 이 재구성된 데이터를 저장하는 장소가 1차 저장소 이며 이 재구성을 위해 필요한 데이터들의 원본이 2,3차 저장소에 적재되어 있는 것이죠.

각 레이어의 데이터를 갱신하는 전략도 살펴봅시다. 각 데이터 저장소를 갱신하게 되는 주체는 이벤트 처리기인 워커 혹은 배치 서버로 부터 시작됩니다. 그리고 3 → 2 → 1의 순서대로 데이터가 갱신됩니다.

이때 데이터 갱신 시 일반적으로 많이 사용하는 전략 처럼 일단 지운 후에 다음 읽기 시점에 갱신하는 전략을 취하지 않았는지 궁금할 수 있습니다. 이는 캐시가 만료된 사이 요청이 몰리게 될 경우 캐시 갱신으로 인해 사용자들에게 느린 응답을 제공할 수 있다는 판단 때문이라고 합니다.

이 사례는 캐시 도입이 언제나 단순히 레이어의 추가로 해결되지 않고, 때로는 저장소의 레벨을 나눈 아키텍쳐를 고려해야 할 수 있다는 걸 배우게 합니다.

마무리하며

제목에서는 레디스 사용법에 대해서 알려줄 것 처럼 어그로를 끌어서 실망하신 분들이 혹 계실까 합니다. 다만 캐시 뿐 아니라 어떤 기술을 선택하든지 기술의 장점에만 이끌려 선택해서는 안되겠죠. 새로운 해결법의 추가는 또 다른 문제를 만들 수 있으니까요. 이 글을 통해 저와 같은 초보 개발자 분들에게 캐시에 대해서 더 깊은 고민을 할 수 있는 기반이 되었기를 바랍니다.

레퍼런스

profile
Code For Christ

0개의 댓글

관련 채용 정보