프로그래밍이야기/Spring

Spring Boot Cache 적용

사랑꾼이야 2022. 1. 30. 23:54
반응형

회사에서 Spring Boot Cache를 활용하여 적용할 기회가 생겨서 진행한 내용을 공유드립니다.

모든 소스는 Github에서 확인이 가능합니다.(링크)

캐시(cache)란

프로그램이 수행될 때 나타나는 데이터 지역성을 이용해 메모리나 디스크에서 사용되었던 내용을 빠르게 접근할 수 있는 곳에 보관하고 관리함으로써 두 번째 접근 부터는 보다 빠르게 참조하도록 하는 것입니다.

여기서 데이터 지역성은 어떠한 뜻을 가지고 있을까요? 그 뜻을 먼저 확인해보면, 다음과 같습니다.

데이터 지역성이란?

  • 데이터 접근이 시간적 혹은 공간적으로 가깝게 일어나는 것을 의미합니다.
  • 한 번 참조된 변수는 잠시 후에 또 참조될 가능성이 높습니다.
  • 어떤 데이터에 접근할 때 그 데이터 근처에 있는 다른 데이터도 참조될 가능성이 높습니다.
즉, 사용되었던 데이터는 다시 사용되어질 가능성이 높다는 개념을 이용하였습니다. 다시 사용될 확률이 높은 것은 더 빠르게 접근 가능한 저장소를 사용한다는 개념입니다.

 

캐시가 동작하는 구조는 개발을 하는 방법의 따라서 아래와 같이 2가지로 나누어지게 됩니다.

구조

여기서는 Look aside cache를 사용하였습니다.

Look aside cache

  1. Web Server는 데이터가 존재하는지 Cache를 먼저 확인합니다.
  2. Cache에 데이터가 있으면 Cache에서 가져옵니다.
  3. Cache에 데이터가 없다면 Database에서 읽어옵니다.
  4. Database에서 읽어온 데이터를 Cache에 다시 저장합니다.

Write Back

  1. Web Server는 모든 데이터를 Cache에만 저장합니다.
  2. Cache에 특정 시간동안의 데이터가 저장됩니다.
  3. Cache에 있는 데이터를 Database에 저장합니다.
  4. Database에 저장된 데이터를 삭제합니다.

 

캐싱과 캐시를 혼용해서 사용하는 경우도 있습니다. 캐싱의 대한 개념은 다음과 같습니다.

캐싱(Caching)

Cache를 사용하는 것을 말합니다.

 

캐시가 사용되는 범위는 다양하게 있습니다. 아래 내용을 확인해보면 됩니다.

사용 범위

운영체제, CDN, DNS 등의 네트워킹 계층, 그리고 웹 애플리케이션 및 데이터베이스를 비롯한 다양한 기술 계층에 걸쳐 적용되고 활용되고 있습니다.

여기서는 웹 어플리케이션에서 캐시에 대해서 다루겠습니다.

 

cache의 개념적인 부분에 대해서는 알아보았습니다. 그러면 cache를 지원하는 Spring에 대해서 알아보도록 하겠습니다.

Spring Cache

  • 버전 3.1 이후 Spring Framework는 기존 Spring 애플리케이션에 캐싱을 추가할 수 있도록 지원합니다.
  • 캐싱 추상화를 통해 코드에 미치는 영향을 최소화하면서 다양한 캐싱 솔루션을 일관되게 사용할 수 있습니다.
  • Spring에서 제공하여 주는 캐시 종류는 9가지가 있습니다. 지원 목록은 다음 내용에서 확인할 수 있습니다.

 

캐시는 다음과 같은 경우에 사용을 하게 됩니다.

사용 예

  • 동일한 데이터를 반복적으로 제공해야하는 경우
  • 데이터의 변경주기가 빈번하지 않고, 처리 시간이 오래걸리는 경우

 

캐싱을 사용하기 위해 예제를 진행해보면서 사용법을 알아보도록 하겠습니다.

개발 환경

  • Java : 1.8
  • Spring Boot : 2.6.2

 

가장 간단하게 구현할 수 있는 simple캐시를 통해서 실습을 진행해보도록 하겠습니다.

Gradle

  • 애플리케이션에 캐시 라이브러리가 없는 경우 기본값인 simple 캐시를 사용하게 됩니다.
  • simple캐시는 ConcurrentHashMap을 캐시 저장소로 사용합니다.

 

먼저 간단한 예제를 통해서 캐시가 동작하는지 살펴보도록 하겠습니다.

캐시 실습1 - 간단한 테스트

  • @Cacheable 은 캐시가 있으면 캐시의 정보를 가져오고, 캐시가 없으면 새로 등록합니다.
  • 간단한 Book 도메인과 컨트롤러, 서비스를 만들었습니다.
    • 도메인
      Book
    • 컨트롤러
      BookController
    • 서비스
      BookService

실행

  • 서버를 시작하여서 캐시가 적용되지 않았을때와 캐시가 적용되었을 때 비교를 해보겠습니다.
  • 실행은 HTTP request 를 이용하여서 진행하였습니다.
    책 단건 조회 - http request

결과

캐시가 적용되지 않았을때

  • 실행을 해보면 아래와 같이 결과를 확인할 수 있습니다.
    책 단건 조회 - http request
  • 캐시가 적용되지 않았기 때문에 쿼리로그는 실행됩니다.
    로그 확인

캐시가 적용되었을때

  • 실행을 해보면 아래와 같이 결과를 확인할 수 있습니다.
    책 단건 조회 결괴 - http request
  • 쿼리 로그는 더이상 실행이 되지 않습니다.

정리

  • 캐시가 적용되지 않았을때와 적용되었을때 가장 큰 차이는 Database가 다운이 되어도 데이터를 가져오는데 있어서 문제가 없습니다.

 

변경주기가 빈번하지 않더라도 변경이 된다면 캐시 데이터는 어떻게 해야 할까요?

이런 변경 사항(추가 또는 삭제)에 대해 메소드를 제공해주고 있습니다.

추가되는 부분에 대해서 먼저 실습을 진행하도록 하겠습니다.

캐시 실습2 - 생성 테스트

  • 서점의 정보가 추가되어야 할 경우 @CacheEvict을 사용할 수 있습니다.
  • @CacheEvict을 사용하면 캐시에서 데이터를 제거하는 트리거로 동작하는 메소드입니다.
  • 키나 조건을 지정해야 할 수 있지만 딱 하나의 엔트리(키에 기반을 둔)가 아니라 제거를 할 캐시의 범위를 나타내는 allEntries 파라미터를 추가로 사용할 수 있습니다.
    • 해당 조건을 통해서 Cache 데이터가 추가된다면 기존 캐시 데이터를 모두 제거하게 됩니다.
  • 캐시 추가 기능을 컨트롤러, 서비스에 작성하였습니다.

실행

  • 서버 실행 후 책의 전체 목록을 가져오는 API 실행하여 캐시 데이터를 저장하고 있겠습니다.
  • 추가된 책을 저장하는 API 실행하여서 추가하고 기존 캐시 데이터를 삭제하겠습니다.
  • 책의 전체 목록을 가져오는 API 실행하였을때 전체 목록을 조회해오는지와 추가된 데이터도 캐싱되는지 확인할 수 있습니다.

결과

  • 책의 전체 목록을 가져오는 API를 2번 실행하여서 캐싱하겠습니다.
    • 실행
      모든 책 조회 - http request
    • 결과
      모든 책 조회 결과 - http request
  • 클린코드 도서를 추가하도록 하겠습니다.
    • 실행
      책 추가 - http request
    • 결과
      책 추가 결과 - http request
  • 책의 전체 목록을 가져오는 API 실행하였을때 전체 목록을 조회해오는지와 추가된 데이터도 캐싱되는지 확인해보겠습니다.
    • 실행
      모든 책 조회 - http request
    • 결과
      모든 책 조회 - http request
  • 아래 로그와 같이 findBookAll 처음 실행되고, 그 다음 createBook 실행되고, 마지막으로 findBookAll 실행됩니다.
    로그 결과
    • @CacheEvict(cacheNames = "books", allEntries = true) 옵션이 적용되어서 첫번째 전체 조회를 통하여 쿼리가 실행이 되었고 그 다음, 책 추가 저장 요청의 대하여 데이터를 Database에 저장하면서 현재 케시 데이터를 제거하였습니다. 다시 전체 조회를 하면서 쿼리가 실행된 것을 확인할 수 있습니다.

캐시 실습3 - 수정 테스트

  • 등록된 서점의 이름 변경이 필요한 상황이 발생하였습니다.
  • @CachePut을 사용하면 캐시를 업데이트 해야하는 경우 사용할 수 있습니다.
  • 캐시 데이터 기능을 컨트롤러, 서비스에 작성하였습니다.

실행

  • 서버 실행 후 bookId의 1번에 등록되어 있는 책을 조회하여 캐싱합니다.
  • 캐싱되어 있는 데이터, 1번에 등록되어 있는 자바의정석에서 클린코드로 변경하겠습니다.
  • 1번에 등록되어 있는 책을 조회하는 API를 실행하였을때 캐싱 초가화 및 쿼리 수행을 하지 않고 변경된 데이터를 확인할 수 있습니다.

결과

  • 책 1번을 조회하여서 캐싱하도록 하겠습니다.
    • 실행
      책 단건 조회 - http request
    • 결과
      책 단건 조회 결과 - http request
      • 다시 시도하면 Database를 조회하지 않고 캐싱된 데이터를 가져오는 것을 확인할 수 있습니다.
      • 위에서 조회한 서점의 이름을 클린코드로 변경하겠습니다.
    • 실행
      책 수정 - http request
    • 결과
      책 수정 결과 - http request
  • 다시 조회 API를 호출하여서 변경된 캐싱 데이터를 잘 갖고 오는지 확인해보겠습니다.
    • 실행
      책 단건 조회 - http request
    • 결과
      책 단건 조회 결과 - http request 
      • 로그 확인을 해보면 다음과 같습니다.
        로그 결과
        • BookService findBook 메소드 시작 : 첫번째 호출
        • BookService updateBook 메소드 시작 : 두번째 호출
        • 마지막에 호출한 내용은 로그로 남지 않는 것을 보면 캐싱된 데이터가 호출되었다는 것으로 알 수 있습니다.

캐시를 정리하면서 마무리하겠습니다.

정리

  • 캐시는 나중에 서버로 요청이 올 결과를 미리 저장해두었다가 빠르게 서비스를 해주는 것을 의미합니다.
  • Spring Cache에서는 추상화를 통해 캐시 솔루션이 변경되어도 코드의 영향을 최소화하면서 기능을 지원합니다.
반응형