@Cacheable

Spring에서 메서드의 실행 결과를 캐시에 저장하고, 동일한 요청이 오면 메서드를 실행하지 않고 캐시된 결과를 반환하는 어노테이션입니다.

동작 흐름

요청 → 캐시에 데이터 있음? → Yes → 캐시된 값 반환 (메서드 실행 X)
                            → No  → 메서드 실행 → 결과를 캐시에 저장 → 반환

기본 사용 예시

@Service
public class ProductService {

    @Cacheable(value = "products", key = "#id")
    public Product getProduct(Long id) {
        // 첫 호출 시에만 DB 조회, 이후 캐시에서 반환
        return productRepository.findById(id)
                .orElseThrow(() -> new NotFoundException("상품 없음"));
    }
}

주요 캐시 어노테이션

어노테이션설명
@Cacheable캐시가 있으면 반환, 없으면 실행 후 저장
@CachePut항상 메서드를 실행하고 결과를 캐시에 갱신
@CacheEvict캐시에서 데이터를 삭제
@Caching여러 캐시 어노테이션을 조합하여 사용
@Service
public class ProductService {

    @Cacheable(value = "products", key = "#id")
    public Product getProduct(Long id) {
        return productRepository.findById(id).orElseThrow();
    }

    @CachePut(value = "products", key = "#product.id")
    public Product updateProduct(Product product) {
        return productRepository.save(product);  // 실행 후 캐시도 갱신
    }

    @CacheEvict(value = "products", key = "#id")
    public void deleteProduct(Long id) {
        productRepository.deleteById(id);  // 삭제 후 캐시도 제거
    }

    @CacheEvict(value = "products", allEntries = true)
    public void clearAllProductCache() {
        // products 캐시 전체 삭제
    }
}

주요 속성

속성설명예시
value캐시 이름"products"
key캐시 키 (SpEL 표현식)"#id", "#product.name"
condition캐싱 조건"#id > 10"
unless결과 기반 캐싱 제외 조건"#result == null"
cacheManager사용할 캐시 매니저 지정"redisCacheManager"
@Cacheable(
    value = "products",
    key = "#id",
    condition = "#id > 0",          // id가 0 이하이면 캐싱하지 않음
    unless = "#result == null"       // 결과가 null이면 캐싱하지 않음
)
public Product getProduct(Long id) {
    return productRepository.findById(id).orElse(null);
}

캐시 저장소 종류

저장소특징
ConcurrentMapCacheSpring 기본 내장, 로컬 메모리, 별도 설정 불필요
Caffeine고성능 로컬 캐시, TTL/최대 크기 설정 가능
Redis분산 환경에서 서버 간 캐시 공유, 가장 널리 사용
EhCacheJVM 기반 로컬 캐시, XML 설정 지원

Redis 캐시 설정 예시

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(30))           // TTL 30분
                .disableCachingNullValues()                   // null 캐싱 방지
                .serializeValuesWith(
                    SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())
                );

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

주의사항

주의점설명
프록시 기반@Transactional과 동일하게 내부 호출(self-invocation) 시 캐시 미적용
키 설계키가 동일하면 다른 파라미터여도 같은 캐시를 반환하므로 키 설계가 중요
직렬화Redis 등 외부 캐시 사용 시 객체가 Serializable이어야 함
캐시 일관성데이터 변경 시 @CacheEvict 또는 @CachePut으로 캐시 동기화 필요
TTL 설정TTL 없으면 오래된 데이터가 계속 남으므로 적절한 만료 시간 설정 필수

CSLOG에서 보기