캡스톤 주제인 “서울특별시 폐수거함 위치 정보 제공 서비스”는 폐수거함을 유형별로 조회하는 것이 메인 기능이다. 프로젝트에서 사용하는 폐수거함(일반 쓰레기통, 재활용 쓰레기통, 의류수거함, 폐건전지 수거함, 폐형광등 수거함, 폐의약품 수거함) 데이터들은 모두 공공데이터포털에서 가져오고 있다. 수집한 6개의 폐수거함 데이터의 갯수는 총 15671개이고, 아래와 같이 전처리 후에 14200개 데이터가 데이터베이스에 적재하였다.
위 화면은 폐수거함을 유형별로 조회하는 화면이다. 의류수거함을 선택하고 조회했을 때 전체 3849개의 의류수거함 데이터를 한꺼번에 가져오게 된다.
이 데이터들은 업데이트가 자주 이루어지지도 않고 자주 호출되는 데이터이기 때문에 계속 DB에 가서 데이터를 가져올 필요가 없다고 생각하였고 데이터양이 많기 때문에 사용자가 불편함을 느낄 수도 있기 때문에 캐시를 활용해서 조회 속도를 개선해보았다.
캐시란 복잡한 연산이나 오래 걸리는 연산을 미리 수행하여 결과 값을 저장해놓고 빠르게 사용하는 것이다. 캐시의 접근 시간에 비해 원래 데이터를 접근하는 시간이 오래 걸리는 경우나 값을 다시 계산하는 시간을 절약하고 싶은 경우(반복적으로 동일한 결과를 돌려주는 경우)에 사용한다.
폐수거함 데이터들은 공공데이터포털에서 가져온 인증된 데이터들이다. 이를 삭제하거나 추가하는 경우는 서비스 내에서 사용자들의 제보에 의해서만 이루어지는데, 이는 빈번하게 이루어지는 작업이 아니다. 그 이유는 사용자 제보가 쌓였을 때 매일 자정에 스케쥴러에 의해서 삭제나 추가가 수행되기 때문에 캐시를 사용하기 적합하다고 생각한다. 또한 이 데이터들은 조회를 원하는 사용자들에게 반복적으로 제공되기 때문에 폐수거함을 읽어온 조회 결과를 캐시 메모리에 저장해둔다면 매번 DB로부터 호출하지 않고 데이터를 가져올 수 있기 때문에 효율적인 READ가 가능하다고 생각하였다.
어떤 캐시 전략이 우리 프로젝트에 적합한지 살펴보았다.
캐시의 종류에는 크게 두 가지가 존재한다.
Local Cache 선택
Global Cache를 사용하는 것은 오버스펙이라고 판단했고, 현재 하나의 서버만을 사용하고 있고 더 빠른 응답 속도 원하기 때문에 SpringBoot에서 제공해주는 여러 캐시를 비교하여 Local Cache를 사용하기로 하였다.
SpringBoot에서는 고맙게도 기본 캐시 기능을 제공해준다. 스프링부트 애플리케이션을 실행하면 해당 애플리케이션과 함께 살아있는 캐시 공간을 사용한다. 이 캐시 공간은 메모리를 차지하므로 많은 캐시를 저장하는 것에는 적합하지 않다. 또한 이 캐시 데이터는 애플리케이션이 죽으면 없어지는 캐시 데이터이기 때문에 애플리케이션이 죽어도 캐시 데이터가 사라지지 않고 기존 캐시 데이터를 사용하고 싶으면 캐싱 서버를 두는 분산 캐싱을 해야 한다.
Spring에서는 고맙게도 기본 캐시 기능을 제공해준다. 지원해주는 캐시 종류는 9가지가 있다. 스프링부트 애플리케이션을 실행하면 해당 애플리케이션과 함께 살아있는 캐시 공간을 사용한다.
Local Cache에는 Caffeine Cache 또는 Ehache가 많이 쓰이는 것 같다. 물론 ConcurrentMap으로 직접 캐시를 구현할 수 있지만, 캐시에 넣어주고 조회하고 제거하는 코드로 인해 지저분해질 수 있기 때문에 Caffeine Cache와 Ehache의 성능 비교를 통하여 선택하기로 하였다.
Caffeine Github wiki에서는 위와 같이 소개하고 있다. Caffeine cache는 High performance Java caching Library이다. 문서를 일겅보면 캐시와 ConcurrentMap과의 차이점에 대한 설명도 덧붙이고 있다. ConcurrentMap에 저장된 데이터는 해당 Map이 제거될 때까지 영구적으로 보관하지만, 캐시는 evict 로직이 자동으로 동작하게끔 구성된다고 한다.
Ehcache도 마찬가지로 Java 진영에서 유명한 Local Cache 라이브러리 종류 중 하나이다. 알아본 바에 따르면, Caffeine cache보다 더 많은 기능을 제공해준다. 분산 처리, Cache Listener 그리고 Off Heap에 캐싱된 데이터를 저장할 수 있다.
위 그림은 Ehcache 공식 문서에 있는 Distributed Caching 관련 내용이다. 각 애플리케이션에 저장되어 있는 캐시 데이터를 Terracotta Server라는 Hub 역할을 하는 분산 캐시 서버에 동기화하는 과정을 볼 수 있다.
아래 자료들은 Caffeine cache GitHub wiki에서 제공하는 자료들이다.
측정 값과 단위 값들은 아래와 같다.
Throughput: 단위 시간당 디지털 데이터 전송으로 처리되는 양
ops/s: operations per second (초당 작업)
Read 100% 성능 측정
Read 75% & Write 25% 성능 측정
Write 100% 성능 측정
Caffeine cache는 EhCache처럼 다양한 기능을 제공하지는 않는다. 하지만 벤치마크 자료를 통해 성능 비교를 하였을 때 심플하게 메모리에 데이터를 캐싱하고 불러오는 작업만 한다면 훨씬 좋은 성능을 보인다는 것을 알 수 있다. 그러므로 우리 서비스에 적합한 Local Cache는 Caffeine cache라고 생각하여 이를 선택하였다.
캐시에 대한 Enum 정의
@Cacheable
에서 정의한 이름)과 만료 시간, 저장 가능한 최대 갯수를 정의하였다.cacheManager를 Bean으로 등록
@Cacheable
애노테이션으로 폐수거함 전체 조회 method 캐싱
데이터 갯수가 가장 많은 의류수거함 폐수거함 조회를 대상으로 테스트를 진행해보았다.
동일한 데이터를 반복적으로 제공하고 업데이트되는 일이 잘 일어나지 않은 폐수거함 데이터들을 Caffeine cache를 통해 캐싱해보았다. 그 결과 응답속도 115ms → 9ms 로 감소 시키는 효과를 거두었다.
Home · ben-manes/caffeine Wiki