프로젝트의 핵심 도메인인 결제/환불(Payment/Refund) 과 쿠폰(Coupon) 시스템에 적용된 동시성 제어(락) 방식을 리뷰하고,
현재 아키텍처의 정합성 평가, 잠재적 문제점, 개선 방안을 정리했다.
결제/환불은 ‘돈’이 오가는 도메인으로 처리량보다 완벽한 데이터 정합성(Consistency) 이 최우선이다.
비관적 락은 같은 결제 건에 대해 동시에 환불/검증 요청이 들어왔을 때 DB 레벨에서 Row Lock으로 직렬화하여 중복 처리를 방지하므로 도메인 특성과 잘 맞는다.
현재 구조의 가장 큰 위험 요소는 락을 쥔 상태에서 외부 API(예: 포트원 등)를 호출한다는 점이다.
@Transactional 내부에서 외부 API를 호출하면 네트워크 지연(Latency) 발생 시목표는 DB 락 점유 시간을 최소화하는 것이다.
PAID -> REFUNDED 등)다만 환불은 외부 API가 중복 호출되면 더 위험할 수 있으므로 아래 안전장치도 함께 고려한다.
REFUND_REQUESTED 같은 중간 상태를 먼저 확보(CAS/락) 한 뒤 외부 호출 → 최종 반영하는 상태머신/사가(Saga) 패턴을 검토한다.선착순 쿠폰 발급은 특정 시간대에 트래픽이 폭발하는 특성이 있다.
이를 DB 비관적 락으로 제어하면 DB가 병목/장애 지점이 되기 쉽다.
멀티 인스턴스(Scale-out) 환경에서 Redis를 단일 진입점으로 삼아 메모리 레벨에서 빠르게 중복 진입을 차단하는 분산 락 전략은 현재 상황에 매우 적합하다.
락 TTL이 3초로 고정되어 있을 경우,
현재 @RedisLock에서 메서드 파라미터 인덱스(argIndex = 0)로 락 키를 생성하면,
MemberCoupon(member_id, coupon_id) 유니크)를 설정해 중복 발급을 원천 차단인덱스 기반 키 생성을 제거하고 SpEL로 명시적으로 키를 구성한다.
@RedisLock(key = "'coupon:' + #couponId")참고: SpEL로
#couponId처럼 파라미터명을 쓰려면 빌드/컴파일 설정에 따라 파라미터명 보존 옵션이 필요할 수 있으므로 테스트로 보장한다.
단순히 “락을 걸어 동시성 이슈를 막았다”에서 끝나지 않고,
선택한 기술이 장애/병목을 만들지, 그리고 미래의 팀원에게 유지보수 리스크를 남기지 않도록 고민하는 과정이 의미 있었다.