
📜 참고 포스팅
Cache에 대해 학습 및 의사결정(Redis vs. MemCached) 과정을 거친 뒤 실제 프로젝트에 적용을 해보려니깐 관련 어노테이션 및 동작에 대한 학습이 부족하다고 판단하여 이번 포스팅을 작성하게 됐습니다.
@Cacheable은 메소드에 캐싱을 적용할 수 있도록 해주기 위한 어노테이션입니다. 메소드의 특정 인자에 대한 결과값을 캐시 메모리에 저장하고 같은 인자에 대한 결과값은 로직을 수행하지 않고, 캐시 메모리에서 조회하여 반환해줍니다.
어노테이션 사용 시, 괄호 안에 해당 메소드와 연관된 캐시의 이름을 명시해줘야 합니다. 이는 캐시에 저장되는 정보를 구분하기 위해 캐시명을 사용합니다.
@Cacheable("books") // 캐시 이름
public Book findBook(ISBN isbn) { // 매개변수 : Key
...
}
위 findBook 메서드는 books라는 캐시와 연동됩니다. findBook 메서드 호출 시 해당 호출 이전에 실행된 적이 있는지 여부를 캐시에서 확인하고 반복 수행을 하지 않도록 해줍니다.
하나의 캐시만 선언하는 대부분의 경우에 어노테이션은 여러 이름을 지정해서 한 캐시를 여러번 사용이 가능합니다.
이러한 경우 메서드 실행 전 각 캐시를 확인할 것이고 최소 하나의 캐시에 저장되어 있다면 해당 값을 반환합니다.
Note
기본적으로 멀티 캐시키 지정은 비활성화되어 있습니다.
@Cacheable({"books", "isbns"})
public Book findBook(ISBN isbn) { ... }
캐시는 K-V 형태의 저장소이므로, 캐시된 메서드를 호출 시마다 해당 키로 변환되어야 합니다.
캐시 추상화는 다음 알고리즘에 기반을 둔 KeyGenerator를 사용합니다.
이러한 방식은 Java의 hashCode() 메서드를 활용하여 객체의 해시값을 계산하는 방식과 유사하며, 대부분의 경우 잘 동작합니다. 이는 객체들을 효과적으로 식별할 수 있는 방법을 제공합니다.
다른 방식으로 기본 키 생성자를 구현하기 위해서는 org.springframework.cache.KeyGenerator 인터페이스를 구현해주면 됩니다.
일반적으로 메서드는 캐싱 구조에 간단하게 매핑할 수 없는 다양한 시그니처를 가질 가능성이 높습니다. 이는 메서드의 여러 개의 매개변수 중 일부만 캐싱을 위해 사용되도록 지정하기 위함입니다.
예를 들면, 다음과 같습니다.
@Cacheable("books")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
위 두 매개변수 중 boolean 인자들의 경우, 책을 찾기 위한 메서드 내부 로직에서 사용되는 것이지 캐시에는 사용하지 않습니다. 일반적으로 key를 별도로 사용할 인자를 구분하기 위해서 Spring의 EL 표현식을 주로 사용합니다.
예를 들면, 다음과 같습니다.
@Cacheable(value="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(value="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(value="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
즉, Cache 메모리에 저장되는 방식은 해시 테이블의 형태이며, 해시 테이블의 이름이 books 내부에 K-V 형태로 값이 저장되고 Key 값으로 사용되는 것이 key 옵션으로 등록된 고유 식별값을 의미합니다.
“books”[
{ 1 - Book(직렬화된 형태)},
{ 2 - Book },
{ 3 - Book },
…
]
메서드 실행에 영향을 주지 않고 캐시를 갱신해야 하는 경우 @Cacheput 어노테이션을 사용할 수 있습니다.
즉, 메서드를 항상 실행하고 그 결과를 캐시에 보관합니다.
하나의 메서드에서 @Cacheable과 @CachePut을 동시에 적용하는 것은 권장되지 않습니다.
캐시 안에 데이터를 제거하기 위한 어노테이션 입니다. 이는 캐시에서 오래되거나 사용하지 않는 데이터를 제거할 때 유용합니다. 일반적으로 캐시를 제거하기 위한 트리거로 동작하는 메서드입니다.
하나의 지역(특정 value)에 전체 캐시를 모두 지워야 하는 경우 allEntries=true 옵션을 사용합니다. 이는 하나의 캐시 테이블 안에 저장된 모든 엔트리(K-V) 를 삭제하는 옵션입니다.
@CacheEvict(value = "books", allEntries=true)
public void loadBooks(InputStream batch)
위는 books로 저장된 모든 엔트리를 삭제합니다.
@CacheEvict 또는 @CachePut 과 같이 캐시 데이터를 수정하거나 삭제 시 여러 개의 캐시 데이터를 지정해야 하는 경우 사용합니다.
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(value = "secondary", key = "#p0") })
public Book importBooks(String deposit, Date date)
위는 primary로 저장된 엔트리 삭제 및 secondary 내 #p0으로 저장된 키값의 엔트리 삭제하는 설정입니다.