스프링 캐시
캐시란?
자주 사용되는 데이터를 저장하는 공간을 의미합니다. 자주 사용되는 데이터를 매번 요청 때마다 생성하여 응답하는 것 보다는 생성된 데이터를 저장해놓고 똑같은 요청이 왔을 때 로직을 거치지 않고 데이터를 반환해주는 것이 서버에 리소스 사용을 줄일 수 있으므로 성능을 향상할 수 있습니다.
<출처: 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
'스터디 > Spring' 카테고리의 다른 글
[Spring] Spring 프로젝트에서 리소스 파일 읽기 (Java8) (0) | 2023.08.11 |
---|---|
[Spring] 스프링 @MockBean, @SpyBean (0) | 2023.03.12 |
[Spring] Filter와 Interceptor 의미와 차이 (0) | 2022.10.06 |
[Spring] @ResponseBody와 @ModelAttribute 차이점 (0) | 2021.08.17 |
[Spring] @NotNull @NotEmpty @NotBlank 차이점 (0) | 2021.08.17 |
댓글