금융권 3-Tier 아키택처에서 Redis를 활용한 고트래픽 대응 방안

🗓️ 2025.07.18 ~ 2025.08.05 1차 기술 세미나

매일 이자 받기 기능에 적용한 Redis 캐싱 전략

계좌 상세 페이지 진입 시 호출되는 '매일 이자 받기' 기능에 Redis 캐싱을 적용했다.
이 기능은 짧은 시간 내에 반복 요청이 많은 특성을 가지므로, 조회 결과를 Redis에 캐싱해 DB 부하를 줄이고 응답 속도를 개선할 수 있다.

📌 읽기 전략: Cache-Aside 패턴 선택

선택 이유 :

  1. 비즈니스 로직 제어 용이: "오늘 이자를 받았는지" 등 조건부 처리 로직을 애플리케이션에서 직접 제어 가능

  2. 장애 격리: Redis 장애 시에도 DB 통해 서비스 지속 가능

  3. 유연한 캐싱 정책: TTL, 키 전략 등 유동적으로 설정 가능

➡️ 복잡한 금융 비즈니스 로직에 적합하고, 장애 대응력과 유연성 확보

Read-Through 대안 검토 결과
-> 복잡함 및 안정성 문제로 부적합 판단
-> 단순 조회에는 적합하지만, 비즈니스 조건이 복잡한 금융 서비스에는 부적합


📌 쓰기 전략: Write-Through 패턴 선택

선택 이유:
DB와 캐시를 동시에 갱신해 데이터 정합성 확보 → 금융 시스템에 적합

📎 캐시 일관성 유지 전략 비교

1️⃣ Update 방식: DB 저장 후 Redis에 바로 갱신 (0으로)

  • 장점: 높은 캐시 히트율, 빠른 응답

  • 단점: 동시성 제어(분산락) 필요, 복잡한 구현

2️⃣ Eviction 방식: DB 저장 후 Redis 캐시 삭제

  • 장점: 구현 간단, 정합성 보장, 동시성 문제 없음

  • 단점: 삭제 후 첫 조회 시 DB 부하 및 응답 지연

➡️ 두 전략 모두 구현해보고 성능 및 안정성 측면에서 비교 분석함

캐싱 전략 구현

1. Cache Update 전략 구현

구현에 앞서, Cache Update 전략의 단점 중 하나는 동시에 여러 요청이 들어올 경우 중복 처리가 발생할 수 있다는 점이다.

[Race Condition 예상 시나리오]
이를 실제 운영 환경에서 발생할 수 있는 시나리오로 구성해봤다.

동일 사용자가 모바일과 웹 브라우저에서 동시에 이자 수령 버튼을 클릭했다고 가정하자. 분산락이 없으면 두 요청 모두 getExistsTodayInterest()에서 "미수령" 상태로 판단해 각각 500원씩, 총 1,000원이 중복 지급된다.

🌟 해결 방법 = 계좌 단위 분산락을 적용
Redis의 setIfAbsent()(SETNX)로 계좌별 락을 획득하고, 첫 번째 요청만 정상 처리한다. 두 번째 요청은 락을 못 잡으면 바로 차단된다. 락 TTL은 5초로 설정해 데드락도 방지했다.

[구현 방법 – RedisTemplate 기반 Cache Update]
Spring Cache(@CachePut)는 간단하게 캐싱을 적용할 수 있지만, TTL을 동적으로 설정하기 어렵고 분산락과 통합할 수 없으며 조건부 캐싱 로직에도 제약이 많다.

그래서 우리는 RedisTemplate을 선택했다. 이 방식은 세밀한 제어 가능,
분산락과의 자연스러운 통합, 동적 TTL 설정 가능이라는 장점이 있다.

핵심 플로우는 다음과 같다.

  • 분산락 획득 – setIfAbsent()로 계좌별 락 설정 (5초 TTL)

  • 비즈니스 로직 실행 – 이자 지급 처리

  • TTL 계산 – 당일 자정까지 남은 시간 계산 후 캐시 TTL 설정

  • 캐시 갱신 – DB 접근 없이 다음 요청 처리

  • 락 해제 – TTL 만료 전이라도 비즈니스 완료 시 즉시 해제

이 흐름을 통해 중복 지급을 막으면서도 DB 부하를 최소화하고, TTL을 상황에 맞게 조정할 수 있다.

2. Cache Evict 전략 구현

[구현 방법 – Cache Eviction]
핵심 개념: 삭제 후 재생성

DB에 이자 지급 내역을 저장한 직후, evictCachedInterest()로 해당 캐시 키를 삭제한다. 이후 최초 조회 시 DB에서 최신 데이터를 불러오고, 이를 기반으로 캐시가 자동 재생성된다.

이 방식은 RedisTemplate.delete()를 사용해 간결하게 DB-캐시 정합성을 맞출 수 있다.

Redis 캐싱 전략에 따른 성능 완화 효과 검증

캐싱 전략이 실제 서비스 부하를 얼마나 완화하는지 검증하기 위해 다음과 같이 성능 측정 환경을 구성했다.

아키텍처 구성

  • K6 – JavaScript 기반 부하 테스트 도구, 실제 사용자 트래픽 시뮬레이션

  • Spring Boot Actuator – JVM 상태, 커넥션 풀, 응답시간 등 핵심 지표 노출

  • Prometheus – 실시간 지표 수집 및 시계열 데이터 저장

  • Grafana – 수집된 데이터를 실시간 대시보드로 시각화

측정 지표

  • 처리량 – 초당 요청 수(RPS), 총 처리 요청 수

  • 응답 시간 – 평균 및 분포

  • 에러율 – 요청 실패 비율

이 환경을 통해 Cache Update와 Eviction 전략이 실제 부하 환경에서 성능을 어떻게 개선하고, DB 부담을 얼마나 줄이는지 검증했다.

자세한 내용은 깃허브로!!
https://github.com/FISA-TechSeminar/Better-Bank-BE/pull/18

성능 테스트 결과 비교 분석

캐싱 전과 cache update, eviction의 전략을 비교해보았다.

🧪 테스트 조건 = 동시 사용자: 100명 / 테스트 지속 시간: 10분

🏷️ 구분📊 총 처리 건수⚡ 평균 처리량 (req/s)📈 성능 향상률특징
1. 베이스라인 – Redis 적용 전23,715건39.0-요청 지연이 두드러짐, 캐싱 최적화 필요성 확인
2-1. Redis 적용 후 – Cache Update 전략59,001건99.2🔼 154%높은 캐시 히트율 유지, 분산락·TTL 계산 로직 복잡, 동시성 제어 부담
2-2. Redis 적용 후 – Cache Evict 전략59,254건96.7🔼 148%구현 단순, 데이터 정합성 보장, 유지보수 용이, Update 전략 대비 2.5 req/s(2.5%) 낮음

Update 와 Evict 전략 특성 비교는 다음과 같다.

🔒 여기서 우리는 의문을 가지게 되었다.
왜 Cache Update가 속도는 더 빠른데 총 처리 건수는 적을까?
🔑 그 이유는 다음과 같다
: Cache Evict 전략은 캐시 적중 시에는 처리 속도가 빠르고 초당 요청 처리량(RPS)도 높아지지만, 캐시 미스가 발생하면 DB까지 조회해야 하므로 응답이 느려지고 RPS가 낮아진다. miss된 요청은 더 많은 처리를 포함하므로 한 번의 요청이 더 많은 sub-request를 유발해 Total Requests는 많아진다.

그라파나로 시각화한 비교

결론

금융권에서 최우선은 데이터 일관성과 신뢰성이다.
성능 차이 2.5%보다 중복 지급·정합성 오류를 원천 차단하는 Cache Evict 전략이 더 적합하다.

회고

이번 세미나를 준비하고 발표하면서 Redis 캐싱 전략을 더 깊이 이해할 수 있었고, 고트래픽 환경의 병목 문제를 직접 다뤄본 좋은 경험이 되었다. 앞으로 이 경험을 바탕으로 최종 프로젝트에도 적용해보고, 실용적인 IT 역량을 키우며 서비스 안정성과 효율성에 기여하는 개발자가 되도록 꾸준히 노력하겠다!

팀 얘기를 하자면, 팀워크가 너무너무 좋아서 정말 재밌게 준비했다. 내 강박과 같은 계획에도 팀원들이 웃으며 함께 계획을 세우고 동참해줘서 고마웠다. 모두가 어느정도 완벽주의라 그런지 서로 잘 맞았던 것 같고 덕분에 의지가 꺾이지 않고 끝까지 최선을 다할 수 있었다. ㅎㅎ 정말 행복한 세미나였고, 덕분에 우승까지 했다~~!


📷

안 풀릴 때마다 과자를 까먹으며 늦게까지 준비하고,

불꽃 여자라는 별명도 얻고,

열심히 발표 준비도 하고,

상도 받고,

끝나고 회식도 했다!

발표 영상을 유튜브에 올렸으니 궁금하면 👉🏻👉🏻 발표 영상 😳

profile
🌱

0개의 댓글