기술과 산업/언어 및 프레임워크
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