Spring Cache와 Redis 통합을 통한 RuoYi-Vue-Plus 캐시 관리

Spring Cache 개요

Spring Cache는 Spring 3.1부터 도입된 캐싱 추상화 프레임워크로, AOP를 활용해 어노테이션 기반 캐싱을 제공합니다. 주요 특징:

  • 코드 변경 최소화: @Cacheable 등 간단한 어노테이션으로 캐싱 구현
  • 다양한 캐시 구현체 지원: CacheManager 인터페이스를 통해 Caffeine, Ehcache 등과 통합
  • JSR-107 표준 호환: Spring 4.1 이상에서 JCache 어노테이션 지원

Spring Cache 핵심 구성요소

인터페이스

  • Cache: RedisCache, ConcurrentMapCache 등 실제 캐시 연산 정의
  • CacheManager: RedisCacheManager 같은 캐시 컴포넌트 관리자
  • CacheResolver: 캐시 해석기 지정

주요 어노테이션

  • @Cacheable: 메서드 실행 전 캐시 확인
  • @CacheEvict: 캐시 데이터 삭제
  • @CachePut: 반환값 캐시 저장
  • @Caching: 다중 캐시 어노테이션 조합

Redis 통합 구현

@Configuration
@EnableCaching
public class RedisIntegrationConfig {
    @Bean
    public CacheManager cacheManager() {
        return new CustomCacheManager();
    }
}

커스텀 CacheManager

public class CustomCacheManager implements CacheManager {
    private final Map<String, Cache> caches = new ConcurrentHashMap<>();
    
    @Override
    public Cache getCache(String identifier) {
        String[] segments = identifier.split("#");
        String baseName = segments[0];
        
        Cache existing = caches.get(baseName);
        if(existing != null) return existing;
        
        CacheConfig settings = new CacheConfig();
        if(segments.length > 1) 
            settings.setExpiration(Duration.parse(segments[1]).toMillis());
        
        Cache newCache = createCacheInstance(baseName, settings);
        caches.put(baseName, newCache);
        return newCache;
    }
    
    private Cache createCacheInstance(String name, CacheConfig config) {
        // Redis 연결 및 캐시 생성 로직
    }
}

캐시 어노테이션 활용 예시

@Cacheable(value = "userData#60s", key = "#userId")
public User fetchUser(String userId) {
    return userRepository.findById(userId);
}

@CacheEvict(value = "userData", allEntries = true)
public void clearUserCache() {}

캐시 유틸리티 클래스

public final class CacheHelper {
    private static final CacheManager MANAGER = ApplicationContext.getBean(CacheManager.class);
    
    public static Object retrieve(String cacheName, Object key) {
        return MANAGER.getCache(cacheName).get(key).get();
    }
    
    public static void store(String cacheName, Object key, Object value) {
        MANAGER.getCache(cacheName).put(key, value);
    }
}

캐시 문제 해결 전략

캐시 Avalanche (Cache Avalanche)

원인: 다수 키의 동시 만료로 인한 DB 부하
해결책:

  • TTL 분산 설정 (예: cache#60s#10m)
  • Redis 클러스터 구성
  • 핫 데이터 영구 캐싱

캐시 Breakdown (Cache Breakdown)

원인: 핫 키 만료 시 동시 요청 급증
해결책:
@Cacheable(sync = true)를 통한 동기화 처리

@Cacheable(value = "hotData", key = "#id", sync = true)
public Data fetchHotData(String id) { ... }

캐시 Penetration (Cache Penetration)

원인: 존재하지 않는 데이터에 대한 지속적 요청
해결책:

  • Bloom Filter를 통한 사전 차단
  • Null 값 캐싱 (예: @Cacheable(unless = "#result == null"))

태그: SpringCache Redis CacheManager Redisson CacheAvalanche

5월 25일 08:33에 게시됨