기술과 산업/언어 및 프레임워크

Spring Boot 시리즈 16편 – 고급 캐시 전략: 멀티 레벨 캐시(L1+L2)와 데이터 일관성 관리

B컷개발자 2025. 4. 27. 15:30
728x90
Spring Boot에서 멀티 레벨 캐시(L1+L2) 전략을 구현하고, 데이터 일관성 문제를 해결하는 방법을 설명합니다. 실전 아키텍처와 보완 설계까지 포함했습니다.

 


 

Spring Boot 시리즈 16편 – 고급 캐시 전략: 멀티 레벨 캐시(L1+L2)와 데이터 일관성 관리

 

API 응답 속도를 높이고, 서버 부하를 줄이는 데에 **캐시(Cache)**는 필수적인 존재입니다.

하지만 단순한 캐시 적용만으로는 변경된 데이터가 반영되지 않거나,

동시성 문제로 인해 잘못된 정보가 노출되는 문제를 막을 수 없습니다.

 

이번 글에서는 Spring Boot 환경에서 멀티 레벨 캐시를 구축하고,

속도 + 일관성 + 복원력을 동시에 확보하는 방법을 실무 사례 기반으로 설명합니다.

 


 

📌 1. 기본 캐시 구조와 한계

항목설명

1단계(L1) 로컬 메모리(Heap) 캐시 (예: Caffeine)
2단계(L2) 분산 캐시(예: Redis)
한계점 변경/갱신 시점 문제, 캐시-DB 간 불일치 발생 가능

 

 


 

✅ 2. 멀티 레벨 캐시(Multi-Level Cache) 개념

 

 

✅ 기본 구성

[요청]
  ↓
[L1 캐시] (Heap 메모리) → MISS → [L2 캐시] (Redis) → MISS → [DB 조회]
  ↓                                    ↓
응답 저장 (L1 + L2 업데이트)

 

  • 1차 캐시(L1): 초고속 응답 (Heap 메모리, 단일 서버 범위)
  • 2차 캐시(L2): 분산 시스템에서 데이터 공유 (Redis 등)

 


 

🛠️ 3. Spring Boot 환경에서 멀티 레벨 캐시 구현

 

 

1️⃣ 의존성 추가 (Gradle)

implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'com.github.ben-manes.caffeine:caffeine'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

 

 


 

2️⃣ 캐시 설정 (application.yml)

spring:
  cache:
    type: caffeine
  redis:
    host: localhost
    port: 6379

 

  • type: caffeine 설정은 기본 CacheManager를 Caffeine으로 설정
  • Redis 연동은 별도로 구성 (L2 캐시 용도)

 


 

3️⃣ L1: Caffeine 캐시 설정

@Configuration
public class CacheConfig {

    @Bean
    public CaffeineCacheManager caffeineCacheManager() {
        CaffeineCacheManager manager = new CaffeineCacheManager();
        manager.setCaffeine(Caffeine.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .maximumSize(5000));
        return manager;
    }
}

 

  • L1 캐시: 읽기 속도 극대화, JVM 메모리 기반

 


 

4️⃣ L2: Redis 캐시 설정

@Configuration
@EnableCaching
public class RedisCacheConfig {

    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30)) // L2는 더 긴 TTL 가능
            .disableCachingNullValues();

        return RedisCacheManager.builder(connectionFactory)
            .cacheDefaults(config)
            .build();
    }
}

 

 


 

🔁 4. 멀티 레벨 캐시 연동 전략

 

Spring Boot 기본 구조만으로는 L1과 L2를 자동으로 병합하지 못합니다.

Custom CacheManager를 만들어 L1(Caffeine) → L2(Redis) 순서로 조회하도록 조정해야 합니다.

 


 

1️⃣ 커스텀 MultiCacheManager 작성

public class MultiCacheManager implements CacheManager {

    private final CacheManager caffeineCacheManager;
    private final CacheManager redisCacheManager;

    public MultiCacheManager(CacheManager caffeine, CacheManager redis) {
        this.caffeineCacheManager = caffeine;
        this.redisCacheManager = redis;
    }

    @Override
    public Cache getCache(String name) {
        Cache caffeineCache = caffeineCacheManager.getCache(name);
        Cache redisCache = redisCacheManager.getCache(name);
        return new MultiLevelCache(caffeineCache, redisCache);
    }
}

 

 


 

2️⃣ MultiLevelCache 작성

public class MultiLevelCache implements Cache {

    private final Cache l1;
    private final Cache l2;

    public MultiLevelCache(Cache l1, Cache l2) {
        this.l1 = l1;
        this.l2 = l2;
    }

    @Override
    public ValueWrapper get(Object key) {
        ValueWrapper value = l1.get(key);
        if (value == null) {
            value = l2.get(key);
            if (value != null) {
                l1.put(key, value.get());
            }
        }
        return value;
    }

    @Override
    public void put(Object key, Object value) {
        l1.put(key, value);
        l2.put(key, value);
    }

    @Override
    public void evict(Object key) {
        l1.evict(key);
        l2.evict(key);
    }

    @Override
    public void clear() {
        l1.clear();
        l2.clear();
    }
}

 

  • 조회 시 → L1 → L2 순서로 조회
  • 저장 시 → L1 + L2 모두 저장
  • 삭제 시 → L1 + L2 모두 삭제

 


 

🧠 실무 캐시 일관성 관리 전략

항목전략

TTL 관리 L1은 짧게 (10분), L2는 길게 (30분) 설정
변경 시점 동기화 데이터 변경 API에서는 반드시 캐시 삭제/갱신
대량 갱신 대응 pub/sub or cache invalidate 기능 활용
장애 대비 L2(Redis) 장애 시에도 L1만으로 임시 대응 가능성 확보
업데이트 알림 L2 업데이트 시 L1에 invalidation push (Advanced)

 

 


 

✅ 마무리 요약

항목정리

기본 구조 L1(Caffeine) + L2(Redis) 멀티 레벨 구성
읽기 흐름 L1 → L2 → DB 순서 조회
쓰기 흐름 L1 + L2 모두 동시 저장
실무 주의 데이터 일관성, TTL 관리, 장애 복원력 고려 필수
고급 설계 Redis pub/sub 기반 캐시 동기화 가능

 

 


 

📌 다음 편 예고

 

Spring Boot 시리즈 17편: 트랜잭션 관리 고급 전략 – 선언적, 프로그래밍 방식, 전파/고립 수준까지 완벽 정리

 

728x90