[Spring] @Casheable 이해하기

2025. 5. 21. 19:28·Spring

본격적으로 들어가기에 앞서,웹 페이지를 이용하다보면 캐시라는 단어를 들어본 적이 있을 것이다.

캐시는 데이터를 빠르게 꺼내쓰기 위해 미리 복사해둔 임시 저장소이다.

이번 글에서는 Spring의 @Cacheable 동작 방식, 캐시의 장단점, 그리고 실무에서 캐시를 적용하는 법을 살펴볼려고 한다.

 

✅Cache 란?

  • 캐시는 데이터 접근 속도를 높이기 위해 원본 데이터를 복사해둔 저장소다.
  • DB나 외부 API를 매번 호출하는 대신 캐시에 저장된 데이터를 재사용해 성능을 끌어올린다.
  • 스프링의 캐시 추상화는 Java 메서드의 반환값을 캐싱하는 데 초점이 맞춰져 있다.
  • @Cacheable 어노테이션을 메서드나 클래스에 붙이면, 메서드 호출 결과가 캐시에 저장돼 다음 호출 시 빠르게 반환된다.
💡 @Cacheable을 클래스에 붙이면 클래스의 모든 메서드에 캐시가 적용된다. 메서드별로 세밀하게 제어하려면 메서드에 붙이는 게 좋다.

 

참고자료:https://docs.spring.io/spring-framework/reference/integration/cache.html

 

Cache Abstraction :: Spring Framework

Since version 3.1, the Spring Framework provides support for transparently adding caching to an existing Spring application. Similar to the transaction support, the caching abstraction allows consistent use of various caching solutions with minimal impact

docs.spring.io

 

✅ Cache를 사용할 때 고려해야할 점

캐시는 성능의 신이지만, 함부로 쓰면 악마가 된다. 실무에서 캐시를 적용할 때 꼭 고려해야 할 점들을 정리해보자.

 

📌 1. 캐시는 만능이 아니다.

캐시는 성능 향상과 서버 부하 감소를 위해 존재한다. 하지만 아무 데나 붙였다간 오버헤드만 늘어난다.

  • 자주 조회되는 데이터에 써야 효과적이다. 예를 들어, 계좌 잔액이나 고객 정보처럼 반복적으로 조회되는 데이터는 캐시로 딱 좋다.
  • 동일한 결과를 주는 경우에만 유효하다. 결제 처리 내역처럼 매번 결과가 바뀌는 데이터는 캐시해봤자 소용없다.
  • TTL(Time to Live, 유효시간)을 신중히 설정해야 함->캐시가 너무 오래 남아 있으면 최신 데이터가 아닌 오래된 데이터를 제공할 수 있다.

 

캐시의 반환값이 일정하지 않의면 의미가 없다.

 

 

📌 2. 로컬 캐시 vs 글로벌 캐시

캐시는 저장 위치에 따라 로컬 캐시와 글로벌 캐시로 나뉜다. 각각의 특징과 장단점을 표로 정리해봤다.

 

 

구분로컬  로컬 캐시 글로벌 캐시
저장 위치 서버 메모리 (내부 저장소) 외부 저장소 (Redis, Memcached 등)
장점 - 메모리 접근이라 속도가 빠르다
- 네트워크 지연 없음
- 서버 간 캐시 공유 가능
- Scale-Out에 유리
- 모든 서버가 동일 데이터 참조
단점 - 서버 간 캐시 공유 불가능
- Scale-Out 시 메모리 부담 증가 (Replication 필요)
- 정합성 문제 (서버마다 다른 결과 가능)
- 데이터 변경 시 모든 서버에 전파 필요
- 네트워크 I/O로 지연시간 발생

 

 

💡  실무에서는 어떻게 캐시를 선정할까?

  • 로컬 캐시: 서버가 1~2개로 소규모고, Scale-Out 계획이 없으면 충분히 유용하다. (ex ) 소규모 결제 시스템의 계좌 조회)
  • 글로벌 캐시(Redis): 서버가 여러 대거나 Scale-Out이 잦은 대규모 시스템에 적합. (ex )  금융 플랫폼의 고객 정보 조회)
  • 하이브리드: 상황에 따라 로컬과 글로벌 캐시를 섞어 쓰기도 한다. (ex )  자주 바뀌지 않는 고객 기본 정보는 글로벌 캐시(Redis), 자주 바뀌는 잔액은 로컬 캐시로 짧은 TTL 설정)

 

Redis 사용 참고자료:https://docs.spring.io/spring-integration/reference/redis.html

 

Redis Support :: Spring Integration

As described in the Enterprise Integration Patterns (EIP) book, a message store lets you persist messages. This can be useful when dealing with components that have a capability to buffer messages (aggregator, resequencer, and others) when reliability is a

docs.spring.io

 

 

✅ Spring 캐시 추상화의 동작 원리

 

Spring Cache

  1. @Cacheable이 붙은 메서드를 호출한다.
  2. AOP에 의해 Cacheable이 달린 클래스는 프록시 클래스가 된다.
  3. 이 클래스에 의해 CacheInterceptor의 invoke 메서드가 호출된다.
  4. 이 메서드 내부에서 실제 Cache Server에 요청을 보내기 위한 다양한 클래스의 메서드가 호출된다.

 

✅주요 어노테이션

📌 @EnableCaching

  • Spring Cache의 어노테이션을 사용하기 위해서 설정 클래스에 이 어노테이션을 달아주어야 한다.
  • 이 어노테이션을 달 때 CacheManager 빈을 함께 등록해주면 된다.
    • 외부 캐시 서버를 사용하거나 여러 개의 캐시 저장소를 사용하는 등의 추가 커스터마이징을 진행할 경우 설정 클래스에 cacheManager 빈을 등록해야 한다.
    • 따로 cacheManager를 빈을 등록하지 않으면 ConcurrentHashMap 기반 로컬 캐시 저장소를 사용하게 된다.

 

📌 @Cacheable

  • 메서드의 반환값을 캐시 아이템에 저장한다.
  • 캐시에 데이터가 존재하면, 캐시의 데이터를 반환한다.
  • 만약 캐시에 데이터가 없으면, 메서드 로직 수행 후 반환값을 캐시에 추가한다.

🚨 주의

  • key, keyGenerator 는 함께 쓰일 수 없다.
  • cacheManager, cacheResolver는 함께 쓰일 수 없다.

 

속성 설명
cacheNames 캐시 이름
value cacheNames의 Alias
key SpEL 표현식을 작성해 캐시 키를 동적 생성하도록 한다.
keyGenerator 사용할 KeyGenerator를 지정할 수 있다.
condition SpEL 표현식으로 조건을 작성하여 true일 경우에만 캐싱이 적용되도록 한다.
unless SpEL 표현식으로 조건을 작성하여 true일 경우에 캐싱이 적용되지 않도록 한다.
cacheManager 사용할 CacheManager를 지정할 수 있다.
cacheResolver 사용할 CacheResolver를 지정할 수 있다.
sync 여러 스레드가 동일한 키에 대한 값을 로드하려고 할 경우, 기본 메서드의 호출을 동기화

 

📌 @CacheEvict

  • 오래되거나 사용하지 않는 데이터를 제거할 때, 데이터가 변경될 때 캐시 아이템을 제거하기 위해 활용한다.
  • 메서드가 트리거로 동작하므로 반환 값이 무시되어 void 메서드에도 사용할 수 있다.
속성 설명
cacheNames 캐시 이름
value cacheNames의 Alias
key SpEL 표현식을 작성해 캐시 키를 동적 생성하도록 한다.
condition SpEL 표현식으로 조건을 작성하여 true일 경우에만 캐싱이 적용되도록 한다.
unless SpEL 표현식으로 조건을 작성하여 true일 경우에 캐싱이 적용되지 않도록 한다.
cacheManager 사용할 CacheManager를 지정할 수 있다.
cacheResolver 사용할 CacheResolver를 지정할 수 있다.
allEntries 캐시에 저장된 값을 모두 제거할지에 대한 여부
beforeInvocation 메서드 수행 전 캐시 데이터 제거를 수행할 지에 대한 여부

 

📌 @CachePut

  • 캐시에 값을 저장하는 용도로만 사용되며, 항상 메서드 로직을 수행하고 캐시 아이템을 저장한다.
속성 설명
cacheNames 캐시 이름
value cacheNames의 Alias
key SpEL 표현식을 작성해 캐시 키를 동적 생성하도록 한다.
condition SpEL 표현식으로 조건을 작성하여 true일 경우에만 캐싱이 적용되도록 한다.
unless SpEL 표현식으로 조건을 작성하여 true일 경우에 캐싱이 적용되지 않도록 한다.
cacheManager 사용할 CacheManager를 지정할 수 있다.
cacheResolver 사용할 CacheResolver를 지정할 수 있다.

 

📌 @Caching

  • 동일한 캐시 어노테이션을 여러 개 선언하고 싶은 경우 사용한다.
  • 조건이나 키 표현 방식에 따라 여러 동작을 수행해야 하는 경우 유용하다.
속성 설명
cacheable 여러 개의 Cacheable 어노테이션 입력
evict 여러 개의 CacheEvict 어노테이션 입력
put 여러 개의 CachePut 어노테이션 입력

 

📌 @CacheConfig

  • 클래스 수준의 어노테이션으로 cacheNames, KeyGenerator 등을 설정하여 공유할 수 있도록 한다.
  • 클래스 내부 캐시 연산 시 이 설정값을 기본으로 가지게 된다.
  • 메서드 레벨에서 재정의하면 덮어쓰여진다.
속성 설명
cacheNames 캐시 이름
keyGenerator 사용할 KeyGenerator를 지정할 수 있다.
cacheManager 사용할 CacheManager를 지정할 수 있다.
cacheResolver 사용할 CacheResolver를 지정할 수 있다.

 

✅ 캐시 적용의 함정과 해결법

  • 캐시는 성능을 뽑아내지만, 잘못 쓰면 골치 아프다. 실무에서 자주 마주치는 문제와 해결법을 정리해보자.

📌 1. 캐시 정합성 문제

문제: 계좌 잔액이 바뀌었는데 캐시가 옛 데이터를 제공한다.

해결

  • @CacheEvict로 데이터 변경 시 캐시 삭제.
  • 이벤트 버스(Kafka, Zookeeper)로 변경 이벤트를 발행해 모든 서버의 캐시 갱신.

 

📌 2. 낮은 히트율

 

문제: 캐시 미스가 자주 발생해 성능 개선이 안 된다

해결

  • 조회 패턴 분석 후 캐시 키 설계 개선. 예: WHERE IN 쿼리는 여러 ID를 한 번에 캐싱.
  • 카나리 배포로 히트율 테스트 후 TTL 조정.

 

📌 3. 캐시 변조

문제: 로컬 캐시 객체를 수정하면 캐시 데이터도 바뀐다.

해결: 불변 객체 사용 또는 JSON 직렬화.

 

 

✅ 결론

Spring의 캐시 추상화는 @Cacheable, @CacheEvict 같은 어노테이션으로 메서드 반환값을 쉽게 캐싱할 수 있다.

로컬 캐시는 빠르지만 Scale-Out에 약하고, 글로벌 캐시는 정합성이 좋지만 네트워크 지연이 있다.

성능과 정합성이 중요한 환경에서는 캐시 히트율, TTL, 정합성을 챙겨야한다.

 

 

다음글은 실습한 코드를 리뷰해보고 Redis 까지 적용해본 사례를 정리할것이다.

'Spring' 카테고리의 다른 글

[Spring]비동기 메시지 큐 (Kafka & RabbitMQ)  (0) 2025.06.20
[Spring]동시성 제어(Redisson)  (2) 2025.06.16
[Spring Security] 스프링 시큐리티와 JWT로 인증 구현하기  (2) 2025.05.15
[Spring]N+1 문제  (2) 2025.05.08
[Spring] 트랜잭션 내부 호출 문제  (2) 2025.05.01
'Spring' 카테고리의 다른 글
  • [Spring]비동기 메시지 큐 (Kafka & RabbitMQ)
  • [Spring]동시성 제어(Redisson)
  • [Spring Security] 스프링 시큐리티와 JWT로 인증 구현하기
  • [Spring]N+1 문제
코딩로봇
코딩로봇
금융 IT 개발자
  • 코딩로봇
    쟈니의 일지
    코딩로봇
  • 전체
    오늘
    어제
    • 분류 전체보기 (152)
      • JavaScript (8)
      • SQL (11)
      • 코딩테스트 (30)
        • Java (15)
        • SQL (13)
      • Java (10)
      • 프로젝트 (30)
        • 트러블슈팅 (10)
        • 프로젝트 회고 (18)
      • git,Github (2)
      • TIL (38)
      • Spring (20)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    java #arraylist #list #배열
    스파르타 코딩 #부트캠프 #첫ot
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
코딩로봇
[Spring] @Casheable 이해하기
상단으로

티스토리툴바