Caffeine Cache (로컬 캐시) 적용하기

June·2022년 10월 30일
2

인프라

목록 보기
1/1

학습 배경

프로젝트 코드들을 보다가 캐시가 적용되어 있는 부분들이 있었다. 재밌는 점은 레디스를 이용해서 캐시를 이용한 부분도 있었고 로컬 캐시를 이용한 부분도 있었다. 로컬 캐시 중에서도 Caffeine Cache가 적용되어 있어서 간단하게 써보면서 이해해보기로 했다.

로컬 캐시 vs 글로벌 캐시

로컬 캐시는 해당 기기에서만 사용되는 캐시다. 간단한 프로그램들을 만들 때 Map 같은 것들을 이용해서 값을 넣어놓고 있으면 복잡한 연산을 거치지 않고 바로 꺼내와서 쓰는 방식과 유사하다. 말 그대로 로컬에서 돌아가기 때문에 네트워크를 탈 필요가 없어 속도가 빠르지만, 분산 시스템에서 데이터 정합성이 깨질 수 있다.

예를들어, 클라이언트가 같은 리소스에 대한 요청을 여러번 보내는 상황에서 A 서버에서는 a 데이터를 반환하는데, B 서버에서는 업데이트된 데이터인 b를 반환하여 서로 다른 상태를 가질 수도 있다. 물론 정합성을 맞춰줄 수 있지만, 이 정합성을 맞추는 것조차 비용이 될 것이다.

글로벌 캐시는 여러 서버에서 하나의 캐시 서버에 접근하여 사용하는 캐시다. 네트워크를 타야하기 때문에 로컬 캐시보다는 느리지만 서버간의 데이터 정합성을 걱정할 필요가 없다.

예전에도 데이터 정합성에 대한 고민을 한적이 있는데 이 부분은 정답이 없는 것 같다. 비즈니스 요구 사항에 따라 어느정도까지 정합성을 맞춰야할지, 얼마나 속도가 중요할지가 달라지기 때문이다. 속닥속닥의 인기글을 기준으로 생각해보자면 데이터가 그리 자주 변경되지 않을 것이고, 또 정합성이 안맞다고 해서 크게 문제가 되지 않을 것이다. 캐시의 만료시기를 그리 길지 않게 잡는다면 Eventual Consistency로 정합성이 맞을 것이다.

캐시는 반복해서 조회해도 동일한 결과를 가져오는 상황에서 유용하게 쓰일 수 있다. 매번 데이터가 달라진다면 오히려 캐시에 저장하거나 캐시를 확인하는 작업 때문에 성능이 좋아지지 않을 수도 있다.

Caffeine Cache

Caffeine Cache 공식문서

로컬 캐시에 Caffeine Cache 또는 Ehache가 많이 쓰이는 것 같다. 나 같은 경우에는 이미 팀 프로젝트에 Caffeine Cache가 쓰이고 있어 선택권이 있는 상황은 아니지만, 이 글을 보면 성능 측면에서는 Caffeine이 더 낫다고 한다.

ConcurrentHashMap으로 직접 캐시를 구현할 수도 있겠지만, 캐시에 넣어주고 조회하고, 제거하는 코드로 원래 코드가 지저분해질 수 있다.

적용해보기

1. 의존성 추가해주기

여기서 springframework 소속의 의존성도 추가해주는데 믿음의 스프링은 여러 구현체들이 있는 캐시들에 대해 당연히 추상화를 해놨다. 따라서 나중에 Ehcache로 바꾼다고 해도 변경 포인트를 최소화해서 바꿀 수 있다.

스프링 캐시는 트랜잭션처럼 AOP로 적용된다.

2. 설정값 만들기

캐시 설정 값들을 정의하는 Enum 클래스를 만든다. yml 파일을 이용해서 일괄 설정을 할 수도 있지만, 각 메서드들마다 캐시되어야 하는 설정 값들이 다르다면 이렇게 조절할 수 있다. 지금 나는 메서드 하나밖에 없어서 설정값을 하나밖에 안만들었다.

3. 설정값 넣어주기

복잡해보이지만 간단하다. 2번에서 만든 설정값들로 캐시들을 만들고, 캐시매니저에 그 캐시들을 넣어주는 것이다.

4. 캐시 적용하기

메서드 호출에 대한 결과값을 캐싱해서 다음에 캐시되어 있다면 꺼내준다. 만약 캐시 히트를 못한 경우 기존 메서드를 실행하고 캐시에 데이터를 추가한다.

캐시는 기본적으로 key-value 형태로 저장된다. 메서드의 파라미터가 캐시의 키가된다. 메서드 파라미터가 없으면 디폴트 값을 키로 사용하고, 메소드의 파라미터가 여러개라면 파라미터들의 hashCode 값을 조합해서 키로 사용한다.

키 값을 지정하는데는 Sring Expression Language가 사용된다.

이런식으로 객체의 하위 속성으로 키 값을 잡을 수도 있고, 캐시가 되는 조건을 걸어줄 수도 있다.

5. 확인해보기

캐시 적용하기 전

캐시 적용 후

쿼리가 한번 나가고 더 이상 나가지 않는 것을 볼 수 있다.

현재 로컬에서 돌리고 있을 때 인메모리 DB를 사용하고 있기 때문에 뚜렷한 차이가 없었지만, AWS 같이 네트워크를 타는 환경이라면 훨씬 뚜렷한 차이를 볼 수 있을 것이다.

6. 번외 - 캐시 비우기

데이터가 바뀌었다면 해당 캐시 역시 바뀌어야 한다. 위에서 말한 것처럼 주기적으로 캐시를 다 비울 수도 있고, 부분적으로 데이터가 바뀔때마다 캐시를 바꿔줄 수도 있다.

이렇게 할 경우 id 값을 키로 가진 캐시가 제거된다.

7. 테스트 코드

기타

  • 캐시를 언제 비우고(evict), 데이터가 업데이트 될 때 맞춰줄 것인지는 실제 프로젝트를 하며 고민해보고 이 글을 업데이트 해야할 것 같다.

  • 다음에는 글로벌캐시와, 로컬캐시와 글로벌 캐시를 혼합한 체인 캐시에 대해서도 찾아봐야겠다.

참고

0개의 댓글