-
인기보관소에 로컬 캐시 적용하기백엔드 : 서버공부/Spring 2025. 8. 7. 16:54728x90
복잡한 쿼리대신 가볍고 빠르게
서비스의 홈 화면에는 사용자에게 인기 보관 지역 정보를 보여주는 기능이 있다. 홈은 사용자가 앱을 켰을 때 가장 먼저 도달하는 화면이고, 그만큼 자주 접근하게 된다.
초기에는 이 데이터를 매번 DB에서 조회하는 방식으로 처리했지만, 홈 화면 특성상 요청 빈도가 높아질수록 불필요한 부하가 발생할 수밖에 없었다. 이 문제를 해결하기 위해 캐시 도입을 고려했다.
빠르고 가벼운 선택 : 로컬 캐시
캐시 도입에 있어 먼저 선택한 것은 로컬 캐시였다. 이유는 다음과 같다.
- 프로젝트에서 이미 락 데이터를 관리하기 위해 Caffeine 캐시를 사용 중이었다.
- 인기 보관소는 실시간으로 변하지 않으며, 일정 주기로만 업데이트되어도 큰 문제가 없다.
- 로컬 캐시는 네트워크 지연 없이 빠르게 데이터를 제공할 수 있다.
외부 캐시(redis 등)보다 설정이 간단하고, 접근 속도가 빠르며, 트래픽이 많은 페이지에 적합하다고 판단했다.
캐시 사용 시 고려해야 할 정합성 문제
로컬 캐시는 빠르다는 장점이 있지만, DB와의 정합성 측면에서는 고민이 필요하다.
캐시된 데이터가 오래된 상태로 유지되면, 사용자에게 부정확한 정보를 보여줄 수 있다. 특히 인기 보관소와 같이 사용자 행동에 따라 값이 달라지는 데이터라면 더 신경 써야 한다. 이를 해결하기 위한 전략을 몇 가지 검토했다.
방안 1: 새로운 예약 발생 시 캐시 무효화
처음 검토한 전략은, 새로운 예약이 발생할 때마다 인기 보관소 데이터를 DB에서 조회해 캐시와 비교하고, 필요 시 캐시를 무효화하는 방식이었다.
이 방식은 데이터 정합성 측면에서는 안전하지만, 새로운 예약이 발생할 때마다 인기 데이터를 조회하게 되므로 결국 캐시의 목적 중 하나인 DB 부하 감소를 실현하지 못한다.
방안 2: TTL 기반 캐시 만료
다음으로 고려한 전략은 Caffeine이 제공하는 TTL 기능을 활용하는 것이다.
expireAfterAccess를 사용해 일정 시간 동안 접근이 없거나, 일정 시간이 경과한 경우 캐시를 만료시키는 방식이다.
이 전략은 일정 수준의 정합성을 유지하면서도, 캐시가 오래된 데이터를 무한히 들고 있지 않도록 제어할 수 있다. 무엇보다, DB 부하를 실질적으로 줄일 수 있다.
최종 선택: TTL 기반 캐시 적용
결국 선택한 방식은 TTL 기반 캐시였다.
유사한 사례로, 멜론이나 다른 음악 스트리밍 서비스에서도 실시간 차트가 아닌, 몇 시간마다 인기 순위를 갱신하는 구조를 채택하고 있다. 사용자 경험을 해치지 않으면서 시스템 리소스를 아끼는 방식이다.
우리 서비스의 경우에도 인기 보관소는 실시간성이 강한 데이터가 아니기 때문에, TTL을 설정해 일정 주기로만 갱신되도록 해도 충분하다고 판단했다.
TTL 설정 방식에 대한 추가 고민
초기에는 scheduler() 설정을 통해 백그라운드에서 주기적으로 캐시를 스캔하고 만료시키는 방식을 도입했다. 하지만 아래와 같은 이유로 이 방식을 제거하고, 접근 기반 만료 방식으로 변경했다.
- Caffeine은 기본적으로 event-driven eviction을 제공한다. 캐시에 접근할 때 TTL을 체크하고, 만료된 경우 데이터를 갱신한다.
- 캐시 키는 단일 값이며, entry도 하나다. 여러 키를 관리하는 구조가 아니기 때문에 메모리 누수 위험이 없다.
- .scheduler()를 사용할 경우 내부적으로 스레드를 할당해 정기적으로 스캔한다. 단일 entry를 위해 별도 스레드를 유지하는 것은 오버헤드다.
결론
정합성과 성능은 종종 충돌하는 목표다. 두 가지를 모두 만족시키는 완벽한 해결책은 없기 때문에, 서비스 특성과 데이터의 성격을 고려해 가장 합리적인 타협점을 선택하는 것이 중요하다.
우리의 경우, 인기 보관소는 실시간성이 크게 요구되지 않는 데이터였고, 로컬 캐시와 TTL 기반 접근 만료만으로도 충분한 사용자 경험과 시스템 안정성을 유지할 수 있었다.
'백엔드 : 서버공부 > Spring' 카테고리의 다른 글
MSA 환경에서 인기 보관소 Top5 캐시 설계기: 문제 정의부터 해법까지 (0) 2025.08.09 Grafana와 Prometheus로 모니터링 구축하기 (0) 2025.08.04 AOP 이해할 수 있다! (0) 2025.07.28 오늘은 필터(Filter) 랑 인터셉터(Interceptor) 알아볼게요 (0) 2025.07.25 별거 없습니다. 프록시가 내 메서드를 무시하는 이유에 대해서. (0) 2025.07.24