(12) Spring Official Guide - Caching Data with Spring

HEYDAY7·2022년 10월 31일
0

Learn Kotlin + Spring

목록 보기
13/25

Caching Data with Spring

https://spring.io/guides/gs/caching/

한줄 요약

@Cacheable annotation을 통해 cache가 어떻게 작동하는지 알아보기

코드 작업 및 Run

이제 기본적인 코드들은 생략하고 핵심 코드들만 기록해둔다.

## BookRepository.kt
interface BookRepository {
    fun getByIsbn(isbn: String) : Book
}

## SimpleBookRepository.kt
@Component
class SimpleBookRepository: BookRepository {

    override fun getByIsbn(isbn: String): Book {
        simulateSlowService()
        return Book(isbn, "Some book")
    }

    fun simulateSlowService() {
        try {
            val time = 3000L
            Thread.sleep(time)
        } catch (e: InterruptedException) {
            throw IllegalStateException(e)
        }
    }
}

우선 일반적인 Repository를 확장해서 쓸 수 있지만, Cacheable이 갖는 이점을 보여주는 환경을 조성하고자 custom하게 작성한다.
get 함수의 경우 "isbn"을 받아 해당 isbn을 갖는 Book Object를 return하는 식이며, simulateSlowService()를 통해 3초의 딜레이를 갖는다.

이후엔 log를 통해 실제로 어떻게 동작하는지 살펴본다.

## AppRunner.kt
@Component
class AppRunner(
        @Autowired private val bookRepository: BookRepository
) : CommandLineRunner {
    val logger: Logger = LoggerFactory.getLogger(AppRunner::class.java)

    override fun run(vararg args: String?) {
        logger.info(".... Fetching books")
        logger.info("isbn-1234 -->" + bookRepository.getByIsbn("isbn-1234"))
        logger.info("isbn-4567 -->" + bookRepository.getByIsbn("isbn-4567"))
        logger.info("isbn-1234 -->" + bookRepository.getByIsbn("isbn-1234"))
        logger.info("isbn-4567 -->" + bookRepository.getByIsbn("isbn-4567"))
        logger.info("isbn-1234 -->" + bookRepository.getByIsbn("isbn-1234"))
        logger.info("isbn-1234 -->" + bookRepository.getByIsbn("isbn-1234"))
    }
}

여기까지만 코드를 작성하고 동작해보면 해당 log들이 3초에 한번씩 출력되기에 6번의 결과가 천천히 출력되는 것을 볼 수 있다. 아직까지는 Cache 설정이 되어있지 않기 때문이다. 따라서 Cachable 하게 get 함수를 바꾼 후 결과를 살펴보자.

Enable caching

캐싱이 동작하도록 하는 코드(아마 최소한의 코드일 것이다.)는 딱 두줄이면 된다.
아래와 같이 getByIsbn의 annotation으로 @Cacheable을, Application class의 annotation으로 @EnableCaching을 넣어주면 된다.

## SimpleBookRepository.kt
@Cacheable("books") // 해당 줄이 추가됨.
override fun getByIsbn(isbn: String): Book {
        simulateSlowService()
        return Book(isbn, "Some book")
    }

## ~Application.kt
@SpringBootApplication
@EnableCaching
class CachingDataWithSpringApplication

이렇게 코드를 추가하고 돌려보면 확연한 차이를 느낄 수 있다. 두번째 log가 출력되는 것과 거의 동시에 나머지 4개의 log도 출력된다. 그 이유는 getByIsbn의 호출이 동일한 파라미터로 전에 일어났었기 때문에 다른 추가적인 연산 없이 바로 그 결과물을 돌려주는 것이다.
이런식으로 Cache는 간단하게 구현할 수 있다. 다만 되게 얕다는 생각이 들어 Cache에 대해 조금 더 알아보았다.

Cache in Spring

우선 Cache는 간단히 말해 자주 사용하는 데이터를, 검색결과 등을 저장해뒀다고 재사용하는 것으로 효율적인 연산과 속도를 얻을 수 있게 하는 기술이다.

Cache Abstraction

Spring에서는 캐시 추상화를 통해 사용이 매우 편리하고, 서비스 코드에 집중할 수 있도록 도와준다.(이 내용은 깊게 들어가면 매우 깊어질 거 같아 여기서는 간략하게만 다룬다.)
이 캐시 추상화에는 Cache Manager가 같이 등장한다. 특정한 설정이 없다면 spring boot가 자동으로 적합은 Cache Manager를 연결하며, 따라서 위에서 진행한 프로젝트는 ConcurrentHashMap을 이용했다. 조금 찾아보니 Cache Manager의 경우에 종류가 꽤나 다양하다.

  • ConcurrentMapCacheManager
  • SimpleCacheManager
  • EhCacheCacheManager
  • CaffeineCacheManager
  • CompositeCacheManager
  • JCacheCacheManager

지금은 여러 종류가 있으며, 필요나 성능에 따라 원하는 것과 연결하면 된다~ 정도로 생각하고 넘어간다.

Cache 관련 annotation

공식 문서에 따르면 Spring's caching abstraction은 5가지 annotations를 제공한다. 각각 간략하게만 알아보자.

@Cacheable : Triggers cache population

method를 cacheable하게 만들어주며, 동일한 argument로 함수가 호출되었을 때 실제로 함수를 호출하지 않고 저장되어 있던 결과를 돌려준다. 또 @Cacheable("name") 이런 식으로 cache의 이름을 설정하기도 한다.

@CachePut: Updates the cache without interfering with the method execution.

저장되어 있는 cache 자체가 업데이트 되어야 할 때 사용한다.

@CacheEvict: Triggers cache eviction.

저장되어 있는 cache 자체를 제거해야 할 때 사용한다. beforeInvocation option이 있어 해당 method 실행 이전에 비워야 하는지, 이후에 비워야 하는지 또한 설정할 수 있다.

return 타입이 Unit인 함수에도 사용할 수 있다!

@Caching: Regroups multiple cache operations to be applied on a method.

여러개의 cache 관련 annotation들을 한번에 모아서 처리해준다.
ex)

@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
fun ~~~~

말 그대로 class level에서 cache name이나, key, manager 등을 설정한다.

@EnableCaching

사실 가장 중요할 수 있으며, cache annotation이 포함되어 있는 프로젝트의 상위 class (ex: Application.kt)에서 해당 annotation이 들어 있어야 cache가 정상적으로 작동한다.


이정도로 Cache를 간단히 알아보았다. 사실 생각보다 간단한 사용은 쉬워 보이며, 이정도의 내용으로도 1차적인 이해를 충분히 얻을 수 있을 것이라고 본다.

마치며

Cache는 효율성의 측면에서 매우 중요한 도구라고 생각한다. 물론 이 Cache를 정말 잘 사용하기 위해서는 Cache Manager들의 특성들부터 시작해서 세세하게 알아볼 것이 아주 많지만, 이 가이드를 통해서 어느정도 깊은 탐구를 시작하기 위해나 기초 배경지식을 쌓은 느낌이다. 또한 프로젝트 수준에서는 어느정도 써먹을 수 있을 것 같다.(물론 이는 Spring에서 Cache를 구현하기가 매우 쉽게 되어 있기에 그런 것 같기도 하다.) 아무튼 유용한 가이드였다.
코드는 여기를 보면 되고, cache 관련해서 참고한 공식문서블로그 글을 같이 링크해둔다.

profile
(전) Junior Android Developer (현) Backend 이직 준비생

0개의 댓글