쿠폰 발급 시스템에서의 분산락 사용

SIHA·2025년 3월 29일

해당 프로젝트에서 Redisson을 사용한 이유

이 프로젝트는 동시성 제어가 필요한 쿠폰 발급 기능을 포함하고 있으며, 이를 해결하기 위해 Redisson 기반의 분산 락을 도입했다.

Redisson은 Redis 위에서 동작하는 Java 기반의 분산 도구로, 분산 락, 캐시, 메시징 기능을 제공하며 tryLock()과 unlock() 같은 친숙한 API를 통해 락을 손쉽게 구현할 수 있게 해준다.

비관락, 낙관락 대신 분산락을 선택한 이유

  • 비관적 락은 DB 레벨에서 락을 걸기 때문에 성능 부담이 크고 병목이 생길 수 있음

  • 낙관적 락은 충돌 시 retry 비용이 크고, 충돌이 빈번한 상황(선착순 발급)에서는 적합하지 않음

  • 분산락 (Redisson 기반)은 Redis의 빠른 응답 속도와 락 TTL, 재시도, 해제 등의 기능 덕분에 트래픽이 많은 이벤트 처리에 최적

Redis만으로 구현하려던 초기 시도와 그 한계

처음에는 Redis의 decrement()를 먼저 호출해서 재고를 차감하고, 이후 DB에 쿠폰을 저장하려 했다. 하지만 이 방식에는 문제가 있었다:

쿠폰 수량을 Redis에서 먼저 줄이는데,
DB에서 유저가 이미 쿠폰을 발급받은 사용자라는 조건이 걸리면 저장이 실패함

이미 줄어든 Redis 재고는 다시 increment()로 되돌려야 했고,
예외 처리 로직이 복잡해지고 신뢰성이 떨어졌음

이러한 한계로 인해 튜터의 조언에 따라 아래와 같은 순서로 전환하게 됐다:

Redisson 도입 후 최종 동작 순서

  1. Redisson으로 분산 락(RLock) 획득
  2. DB에서 이벤트 유효성 및 중복 발급 여부 확인
  3. Redis에서 현재 수량을 조회 (get) → 재고가 1 이상인지 확인
  4. Redis에서 수량 차감 (decrement)
  5. DB에 쿠폰 저장
  6. 예외 발생 시 적절한 응답 반환

Redisson vs Redis 직접 구현

redis는 직접 setnx/set 등을 조작해 락을 획득해야하지만 Redisson은 tryLock(), unlock()으로 간단히 획득하능하다.
또한 코드가 더 간결해진다.

이 프로젝트는 단일 서버 기반의 개발/학습 환경에서 동작하므로, Redisson의 여러 모드 중 가장 단순한 Single Server Mode를 사용했다.

RedissonConfig 설정은 공식문서에 따르면 다음과 같다.

RLock이란

Redisson이 제공하는 분산 락 인터페이스 중 하나로, java.util.concurrent.locks.Lock과 유사한 구조를 가지고 있다.

주요 메서드

  • tryLock(long waitTime, long leaseTime, TimeUnit unit)

    	-`waitTime`: 락 획득을 시도하는 최대 시간
    
    	- `leaseTime`: 락이 자동 해제되는 시간 (죽은 락 방지)

unlock(): 락 해제

profile
뭐라도 해보자

0개의 댓글