@Cacheable 동작 원리 사용법 만료시간 unless value example

 

@Cacheable의 핵심 동작 원리와 내부 메커니즘

Spring의 @Cacheable은 AOP(Aspect Oriented Programming)를 기반으로 동작합니다. 클라이언트가 메서드를 호출하면 프록시 객체가 이를 가로채어 캐시 저장소에서 데이터를 먼저 조회합니다.

  1. 캐시 히트(Cache Hit): 저장된 키(Key)가 존재하면 메서드 본문을 실행하지 않고 즉시 값을 반환합니다.

  2. 캐시 미스(Cache Miss): 저장된 데이터가 없으면 실제 메서드를 실행한 뒤, 그 결과값을 캐시에 저장하고 반환합니다.

2026년 현재 고성능 백엔드 설계에서는 단순 로컬 캐싱을 넘어 Redis나 Caffeine과 같은 외부 캐시 솔루션을 결합하여 네트워크 지연과 DB 부하를 동시에 관리하는 것이 표준입니다.


@Cacheable 주요 속성 및 사용법

효율적인 캐싱 전략을 위해서는 각 속성의 역할을 정확히 이해해야 합니다.

  • value (또는 cacheNames): 캐시의 이름을 지정합니다. (예: specialHoliday)

  • key: 캐시 저장 시 사용할 키를 지정합니다. 지정하지 않으면 파라미터 기반으로 자동 생성됩니다.

  • unless: 특정 조건일 때만 캐시에 저장하지 않도록 설정합니다. (결과값이 null이거나 특정 상태일 때 유용)

  • condition: 메서드 실행 전에 캐시 적용 여부를 결정합니다.


구체적인 코드 활용 예시 (unless, condition 포함)

단순 조회를 넘어 실무에서 자주 발생하는 특정 상황(결과가 비어있을 경우 제외 등)을 처리하는 예시 코드입니다.

Java
@Override
@Cacheable(
    value = "specialHoliday", 
    key = "#year", 
    unless = "#result == null || #result.isEmpty()"
)
public List<SpecialHoliday> getCacheSpecialHolidayList(String year) {
    // 결과가 null이거나 비어있는 리스트라면 캐싱하지 않음
    return etcDao.selectCacheSpecialHolidayList(year);
}

💡 주요 옵션 상세 설명

옵션설명예시
value저장될 캐시 영역의 이름value = "holiday"
unless메서드 실행 후 결과에 따른 캐싱 방지unless = "#result == null"
condition메서드 실행 전 입력 값에 따른 캐싱 여부condition = "#year.length() == 4"

캐시 만료 시간(TTL) 및 정합성 관리

@Cacheable 자체에는 만료 시간을 직접 설정하는 속성이 없습니다. 2026년 기준 표준 방식은 사용 중인 CacheManager 설정을 통해 TTL(Time To Live)을 제어하는 것입니다.

  • Redis 설정: RedisCacheConfiguration을 통해 각 캐시 이름별로 다른 TTL을 적용합니다.

  • Caffeine(로컬): CaffeineCacheManager 설정에서 expireAfterWrite를 사용하여 시간 기반 만료를 설정합니다.

  • 데이터 정합성: 데이터가 업데이트되는 시점에는 @CacheEvict를 호출하여 반드시 기존 캐시를 삭제하거나 @CachePut으로 갱신해야 합니다.


자주 묻는 질문 (FAQ)

Q1. @Cacheable을 적용했는데 왜 DB 쿼리가 계속 나갈까요?

동일 클래스 내에서 내부 호출(Self-invocation)을 한 경우 AOP 프록시가 작동하지 않습니다. 반드시 외부 Bean에서 호출하거나 구조를 분리해야 캐싱이 적용됩니다. 또한 @EnableCaching 설정이 누락되었는지 확인하십시오.

Q2. unless와 condition의 차이점은 무엇인가요?

condition은 메서드 실행 전에 파라미터를 기준으로 캐싱 여부를 판단하고, unless는 메서드 실행 후 결과값(#result)을 보고 캐시 저장 여부를 결정합니다. 결과가 null일 때 캐싱을 피하려면 unless를 사용해야 합니다.

Q3. 캐시 만료 시간을 어노테이션에서 직접 조절할 수 없나요?

Spring 표준 어노테이션에서는 지원하지 않습니다. 이는 캐시 공급자(Redis, Ehcache 등)의 독립성을 유지하기 위함입니다. 각 캐시 솔루션의 Configuration 클래스에서 Bean 설정을 통해 관리하는 것이 올바른 아키텍처입니다.

Q4. 여러 개의 파라미터 중 특정 값만 키로 사용하고 싶다면 어떻게 하나요?

key = "#paramName" 형식을 사용하면 됩니다. 만약 여러 파라미터를 조합해야 한다면 key = "#p0 + '_' + #p1" 처럼 SpEL(Spring Expression Language)을 활용하여 커스텀 키를 생성할 수 있습니다.


@Cacheable 핵심 요약

  1. 프록시 기반 동작: 내부 호출 시 캐시가 작동하지 않으므로 주의가 필요합니다.

  2. 조건부 캐싱: unless와 condition을 적절히 혼합하여 불필요한 데이터(null 등)가 메모리를 점유하지 않도록 설계합니다.

  3. 만료 전략: TTL은 CacheManager 설정에서 전역 또는 이름별로 관리하며, 데이터 수정 시 @CacheEvict로 정합성을 맞추는 것이 필수입니다.

댓글

이 블로그의 인기 게시물

쿠팡플레이 맨시티 AT마드리드 중계 일정 가입방법 앱 다운로드 고객센터 오류 해결

해외축구 이적설 월드컵 이슈 확인 토트넘 토날리 리버풀 디오망데 일본 16강 경우의 수

2026 월드컵 A조·B조 전망|대한민국의 첫 관문은 체코전이다