제목: "[ SpringBoot ] SpringBoot의 기본 Cache 사용하기"
작성자: tistory(파미페럿)
작성자 수정일: 2021년7월24일
링크: https://pamyferret.tistory.com/8
작성일: 2022년8월16일
gradle
implementation 'org.springframework.boot:spring-boot-starter-cache'
@EnableCahging
이라는 어노테이션을 달아준다물론 이
@EnableCaching
어노테이션을 스프링부트 어플리케이션 부분에 붙이지 않고 따로 설정파일 역할을 하는 클래스에 생성해서 사용하거나, XML 파일에 별도로 캐싱기능을 사용할 부분에 대해CacheManager
빈을 만들어 좀 더 세부적으로 설정할 수 있는 것 같지만 우선 이번에는 간단하게 캐싱 기능을 사용해보고 익히는 것이므로 그냥 스프링 어플리케이션에 어노테이션을 붙여 해당 어플리케이션에 캐싱 기능을 모두 활성화하는 것으로 하겠다.
위의 기본 설정을 끝냈으면 이제 스프링부트의 기본 캐싱 기능을 사용할 수 있다.
간단하게 계정 정보를 저장하는 테이블을 생성하였다.
AccountService에 모든 계정 조회 하는 메서드를 생성한다.
@Slf4j
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
public class AccountService {
private final AccountJpaRepository accountJpaRepository;
public List<Account> getList() {
return accountJpaRepository.findAll();
}
위 메서드를 호출할때마다 select를 하는것을 확인할 수 있다.
@Cacheable
어노테이션은 처음에 캐시 데이터를 만들고, 그 다음부터는 DB에서가 아닌 캐시에서 데이터를 가져오도록 설정하는 어노테이션이다.
@Cacheable
에는 여러 파라미터 옵션이 있지만, 일단 기본 사용법을 알아보자
@Cacheable("account")
public List<Account> getList() {
return accountJpaRepository.findAll();
}
@Cacheable(value = "account",key = "#id")
public Account get(Long id) {
return accountJpaRepository.findById(id).orElseThrow(() -> new RuntimeException("조회 실패"));
}
@Cacheable
어노테이션은 위와 같이 기본으로 텍스트 값을 넣어줘야하는데 그게 바로 캐시 데이터 저장 공간의 이름이 된다.
물론 value, key 파라미터로 명시를 해줄 수 있다. value
는 캐시 데이터의 이름이 되고 key
는 캐시 데이터의 키 값이 된다.
get
에서는 key
를 '#id'
라고 지정을 해놨는데 이럴 경우 get(Long id)
에서 받는 파라미터 id 값대로 캐시 데이터가 저장되어 추후 각 캐시 데이터를 key 값에 따라 부분 업데이트를 할 수 있다.
즉, 아래와 같이 menu안에 key value와 같이 캐시 데이터가 저장되는 것이다.
만일 키를 따로 설정을 안 하면 해당 메소드의 파라미터로 들어오는 값들로 키가 설정된다.
위와 같이 캐싱 기능을 사용하겠다고 설정을 하고 다시 여러 번 호출하면 아래와 같이 select는 처음 한 번만 이뤄지고 그 후에는 select가 이뤄지지 않는 것을 확인할 수 있다.
즉, @Cacheable
어노테이션으로 캐싱 기능을 사용하게 되었고, 이로 인해서 처음에는 DB에 가서 데이터를 읽어 'account'라는 이름으로 캐시를 저장하지만 그 후에는 DB에서 데이터를 읽지 않고 저장해놓은 캐시 데이터를 읽는 것이다. 이로 인해 DB로 가는 시간을 줄일 수 있게 되었다.
만일 DB의 데이터가 업데이트되면 자동으로 캐시 데이터를 업데이트를 해주지는 않는다.
@Cacheable
은 처음에 캐시에 데이터를 넣으면 그 후로 DB가 변경되든 말든 신경 쓰지 않고 저장된 캐시 데이터만 가져온다.
위의 리스트를 가져왔던 캐시 데이터의 경우 부분 캐시 데이터 부분 업데이트보다는 캐시데이터를 전체 삭제해야하므로 @CachePut
예시에서는 계정 하나를 가져오는 아래 함수를 통해 캐시 데이터 부분 업데이트를 해본다.
// 계정 업데이트 하기
@Transactional
@CachePut(value = "account", key = "#dto.id")
public Account update(AccountUpdateDto dto) {
Account account = accountJpaRepository.findById(dto.getId()).orElseThrow(() -> new RuntimeException("조회 실패"));
account.changeName(dto.getName());
return account;
}
위 코드를 보면 @CachePut(value = "account", key = "#dto.id")
라고 어노테이션을 설정해놓은 것을 확인할 수 있다.
여기서 value는 위에서 설명했듯이 캐시 데이터 이름이다. 위에서 @Cacheable
에 'account'
라고 설정을 해놨으니 똑같이 'account'
라고 설정을 해줘야 거기서 데이터를 읽을 수 있다.
여기서 중요한 것은 key 값인데 이 key 값이 위의 get(Long id)
에서 id를 key 값으로 해서 캐시 데이터를 저장했으므로 update(AccountUpdateDto dto)
에서도 똑같이 해당 id 값의 캐시를 업데이트 해야한다.
그러면 key 값을 똑같이 id로 해야하는데 이번에는 파라미터를 Long 형식이 아닌 별도 지정한 DTO인 AccountUpdateDto로 받아온다.
이와 같이 dto 안의 값을 key 값으로 사용해야할 경우에는 '#dto.id'
와 같이 해당 'dto.${field}'
형식으로 작성을 해주면 된다.
참고:
key와 cache 어노테이션의 추가 파라미터인 condition(캐싱 조건)에 쓰는 문법은 spel 문법이다.
https://www.baeldung.com/spring-expression-language
가끔가다 보면 캐시 데이터를 업데이트 했는데 다시 캐시 데이터를 읽어보면 null 값이 나온다는 경우가 있다. 그것은 캐시 데이터를 업데이트 해주는 곳에서 업데이트 해줄 캐시 데이터를 return 하지 않기 때문이다.
// 메뉴 업데이트 하기
@CachePut(value = "menu", key = "#menuRequest.id")
public void updateMenu(MenuRequest menuRequest) {
...
}
즉, 위와 같이 함수의 반환형을 void로 해놓고 아무 데이터도 반환하지 않으면 캐시에 저장될 데이터 또한 없어 해당 key 값의 캐시 데이터는 null로 업데이트가 되어 버린다.
따라서 아래와 같이 굳이 반환할 필요가 없는 함수여도 캐시 데이터 저장을 위해 캐시 데이터에 저장해줄 값을 반환해야 한다.
// 메뉴 업데이트 하기
@CachePut(value = "menu", key = "#menuRequest.id")
public MenuList updateMenu(MenuRequest menuRequest) {
....
return menuList;
}
// 메뉴 하나 삭제하기
@CacheEvict(value = "menu", allEntries = true)
public void deleteOneMenu(int id) {
this.menuListRepository.deleteById(id);
}
@CacheEvict
어노테이션을 보면 allEntries라고 눈에 띄는 파라미터가 있는데 이 파라미터를 true로 설정하면 어노테이션이 붙어 있는 메소드에서 어떤 파라미터를 받더라도 그냥 해당 캐시 데이터를 다 삭제해버린다.
즉, 위에서 id 값을 파라미터로 받아 id를 key 값으로 저장해놓은 캐시 데이터를 구분할 수 있더라도 'allEntries = true'로 인해 'menu' 안의 모든 캐시 데이터가 지워지는 것이다.
즉, @CacheEvict 어노테이션으로 인해 menu 캐시 데이터가 모두 지워져 다시 DB에서 데이터를 가져와 menu 캐시 데이터로 저장했다는 것을 알 수 있다.
만일 위에서 캐시 데이터의 key 값을 id로 지정한 상황에서 해당 id 값의 캐시만 지우고 싶다면 아래와 같이 어노테이션을 사용하면 된다.
@CacheEvict(value = "menu", key = "#id")
그러면 해당 key 값에 대한 캐시 데이터만 삭제가 되고 menu 캐시에 있는 다른 데이터들은 남아 있다.
캐싱 기능을 사용하다보면 여러 개의 key 또는 캐시 기능을 사용해야할 때가 있다. 그럴 때는 어노테이션을 아래와 같이 작성해주면 된다.
@Caching(
cacheable = {
@Cacheable( value = {"name1", "name2"}, key = "key"),
@Cacheable("name3")
}
)
위와 같이 어노테이션을 작성하면 @Cacheable
이 총 2개가 동작하게 되고 그 중 하나는 name1, name2에 각각 key라는 key 값을 가지게 캐시 데이터가 저장되고 name3에는 key 값을 별도 지정하지 않은 캐시 데이터가 저장된다.