본문 바로가기
스터디/Spring

[Spring] 스프링 캐시 알아보기 (@Cacheable, @CachePut, @CacheEvict)

by zoodi 2023. 3. 12.
728x90

스프링 캐시

캐시란?

자주 사용되는 데이터를 저장하는 공간을 의미합니다. 자주 사용되는 데이터를 매번 요청 때마다 생성하여 응답하는 것 보다는 생성된 데이터를 저장해놓고 똑같은 요청이 왔을 때 로직을 거치지 않고 데이터를 반환해주는 것이 서버에 리소스 사용을 줄일 수 있으므로 성능을 향상할 수 있습니다.

 

<출처: https://mangchhe.github.io/springboot/2021/09/15/SpringBootCache/>

 

그렇다면 캐시는 어디에 사용하는 것이 좋을까요?

  • 클라이언트에게 전달되는 값이 동일할 때
  • 빈번하게 호출될 때
  • 한 번 처리할 때 많은 서버 리소스를 요구 할 때

예시로는 공지사항, 조회수, 랭킹 등이 캐시를 많이 적용합니다.

반대로 캐시를 적용하지 말아야하는 경우는 언제일까요?

  • 실시간으로 정확성을 요구하는 경우
  • 빈번하게 데이터 변경이 일어나는 경우

spring-boot-starter에서 제공하는 캐시는 서버를 끌 때 데이터가 날아가므로 계속 유지하고 싶다면 redis와 같은 외부 저장소를 이용합니다.

 

스프링에서 스프링 AOP기반으로 캐시가 작동하며 어노테이션으로 aop를 설정할 수 있어 간편하게 사용할 수 있습니다. @Cacheable, @CachePut, @CacheEvict를 사용하면 쉽게 캐시 사용을 할 수 있습니다.

스프링 설정

  • build.gradle
implementation 'org.springframework.boot:spring-boot-starter-cache

@Cacheable

@Cacheable은 캐시 생성 및 전달을 담당합니다. 캐시에 데이터가 없을 경우에는 기존의 로직을 실행 후 캐시에 데이터를 추가하고, 캐시에 데이터가 있으면 캐시의 데이터를 반환합니다.

 

 

  • 예시 코드
// 캐시 저장
  @Cacheable("memberCacheStore")
  public Member cacheable(String date) {
    System.out.println("cacheable 실행");
    ...
    return member;
  }

  // 캐시 저장 (Key를 지정한 경우)
  @Cacheable(value = "memberCacheStore", key = "#member.name")
  public Member cacheableByKey(Member member) {
    System.out.println("cacheable 실행");
    ...
    return member;
  }

  // 조건부 캐시 저장 (With Condition)
  @Cacheable(value = "memberCacheStore", key = "#member.name", condition = "#member.name.length() > 5")
  public Member cacheableWithCondition(Member member) {
    System.out.println("cacheableWithCondition 실행");
    ...
    return member;
  }

1) key를 지정하지 않은 경우

  • 파라미터인 date 값에 2023-01이 처음으로 입력된 경우 파라미터  2023-01에 대한 캐싱된 데이터가 없으므로 메서드가 실행합니다.
  • "cacheable 실행" 이라는 문자열이 출력됩니다.
  • 그리고 반환 값인 member가 캐시 저장소에 저장됩니다.
  • 다시 2023-01 을 파라미터에 넣고 요청합니다.
  • 캐싱되어있으므로 캐시 저장소에 저장된 member 값이 반환됩니다.
  • "cacheable 실행"이라는 문자열은 출력되지 않습니다.
  • 2) key를 지정하는 경우
    • key인 name이 "hyeri"인 member객체가 처음 들어오면 캐싱 전이므로
    • "cacheable 실행" 이라는 문자열이 출력되고
    • "hyeri"라는 name을 가진 member 객체 데이터는 캐싱됩니다.
    • "hyeri"라는 name을 가진 멤버 객체다 다시 요청되면 캐싱된 멤버 데이터 객체가 반환되고
    • "cacheable 실행" 이라는 문자열이 출력되지 않습니다.
  • 3) 조건부 캐싱
    • @Cacheable 어노테이션에 condition 속성을 통해 적용 가능합니다.
    • 해당 예시에서는 member객체의 name 필드 값의 길이가 5를 초과하는 경우에만 캐싱되도록한 것입니다.

@CachePut

@CachePut은 캐시 내용 수정을 담당합니다. @Cacheable과 유사하게 실행 결과를 캐시에 저장하지만, 조회 시에 저장된 캐시 내용을 사용하지 않고 항상 메서드를 실행한다는 것이 차이점입니다.

 

  • 예시코드
// 캐시 저장 (Cacheable과 유사하게 실행 결과를 캐시에 저장하지만, 조회 시에 저장된 캐시 내용을 사용하지는 않고, 항상 메소드의 로직을 실행한다는 점에서 다르다.)
  @CachePut(value = "memberCacheStore", key = "#member.name")
  public Member cachePut(Member member) {
    System.out.println("cachePut 실행");
    ...
    return member;
  }

메서드 실행에 영향을 주지 않고 캐시를 갱신해야되는 경우에만 사용됩니다. 즉 같은 name이 들어오더라도 "cachePut 실행"이 계속 출력되면서 member 객체에 데이터가 캐싱됩니다.

 

@CacheEvict

@CacheEvict은 캐시 삭제를 담당합니다. @CacheEvict에 캐시 이름을 넣어주면 메소드가 실행될 때 캐시의 내용이 제거됩니다. 기본적으로 메소드의 키에 해당하는 캐시만 제거합니다.

 

  • 예시코드
  • 1), 2)의 경우
    • key에 해당하는 date 값에 대한 member 객체 데이터가 캐싱되어 있을 경우 해당 키에 대한 요청이 들어오면 메서드가 실행되며 저장된 캐시를 삭제합니다.
  • 3) allEntries 속성
    • 캐시에 저장된 값을 모두 제거할 필요가 있을 때 사용합니다.
  • beforeInvocation 속성
    • 메서드 실행 이후(기본값)나 이전에 캐시를 제거해야하는 지정가능합니다.
    • 메서드 실행 이전에 제거되는 경우 (beforeInvocation=true)에는 메서드가 호출되기 전에 캐싱된 데이터가 항상 제거되므로 메서드 결과에 의존하지 않는 경우에 유용합니다.
// 캐시 제거
  @CacheEvict("memberCacheStore")
  public Member cacheEvict(String date) {
    System.out.println("cacheEvict 실행");
    ...
    return null;
  }

  // name 키 값을 가진 캐시만 제거
  @CacheEvict(value = "memberCacheStore", key = "#member.name")
  public Member cacheEvictByKey(Member member) {
    System.out.println("cacheEvictByKey 실행");
    ...
    return member;
  }

  // 캐시에 저장된 값을 모두 제거할 필요가 있을 때
  @CacheEvict(value = "memberCacheStore", allEntries = true)
  public Member cacheEvictAllEntries() {
    System.out.println("cacheEvictAllEntries 실행");
    ...
    return null;
  }

  // beforeInvocation 속성으로 메서드 실행 이후(기본값)나 이전에 제거를 해야하는지 지정할 수 있다.
  @CacheEvict(value = "memberCacheStore", beforeInvocation = true)
  public Member cacheEvictBeforeInvocation() {
    System.out.println("cacheEvictBeforeInvocation 실행");
    ...
    return null;
  }

출처

https://www.baeldung.com/spring-cache-tutorial

https://velog.io/@bey1548/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%BA%90%EC%8B%9CCacheable-CacheEvict

https://wildeveloperetrain.tistory.com/119

728x90

댓글