대용량 트래픽에서 안전하고 효율적인 선착순 쿠폰 발급을 위한 Spring Boot 기반 시스템입니다.
선착순 이벤트 서비스 개발 시 발생하는 대표적인 문제들을 해결한 고성능 쿠폰 발급 시스템입니다.
- 동시성 문제: 정해진 수량보다 많은 쿠폰이 발급되는 Race Condition
- 성능 문제: 대량 트래픽으로 인한 서버 다운 및 응답 지연
- 중복 발급: 한 사용자가 여러 개의 쿠폰을 발급받는 문제
- 안정성: 시스템 장애 시 쿠폰 발급 누락 방지
- ⚡ 고성능 동시성 제어: Redis INCR을 통한 원자적 연산
- 🔒 중복 발급 방지: Redis Set을 활용한 사용자별 발급 제한
- 📊 비동기 처리: Kafka를 통한 부하 분산
- 🔄 장애 복구: 실패한 쿠폰 발급 재처리 시스템
- 🎯 정확한 수량 제어: 선착순 100명 정확히 제한
- Java 17
- Spring Boot 3.x
- Spring Data JPA
- Spring Kafka
- MySQL: 쿠폰 데이터 영구 저장
- Redis: 동시성 제어 및 중복 방지
- Apache Kafka: 비동기 쿠폰 발급 처리
- JUnit 5
┌─────────────────┐ ┌──────────────┐ ┌─────────────────┐
│ Client │────│ API │────│ Redis │
│ (Multiple │ │ Server │ │ (INCR/SET) │
│ Requests) │ │ │ │ │
└─────────────────┘ └──────────────┘ └─────────────────┘
│
▼
┌──────────────────┐
│ Kafka │
│ Producer │
└──────────────────┘
│
▼
┌──────────────────┐ ┌─────────────────┐
│ Kafka │────│ MySQL │
│ Consumer │ │ (Coupon DB) │
└──────────────────┘ └─────────────────┘
│
▼
┌──────────────────┐
│ Failed Event │
│ Handler │
└──────────────────┘
문제: 여러 스레드가 동시에 쿠폰 개수를 확인할 때 정해진 수량을 초과하여 발급
해결책: Redis INCR 활용
public void apply(Long userId) {
long count = couponCountRepository.increment(); // 원자적 연산
if (count > 100) {
return;
}
couponCreateProducer.create(userId);
}
문제: 한 사용자가 여러 개의 쿠폰을 발급받을 수 있음
해결책: Redis Set 자료구조 활용
public void apply(Long userId) {
Long apply = appliedUserRepository.add(userId); // Redis SADD
if(apply != 1) {
return; // 이미 발급받은 사용자
}
// 쿠폰 발급 로직...
}
문제: 대량 트래픽으로 인한 서버 과부하
해결책: Kafka를 통한 비동기 처리
- Producer: 빠른 응답으로 사용자 경험 개선
- Consumer: 안정적인 쿠폰 발급 처리
문제: Consumer에서 에러 발생 시 쿠폰 발급 누락
해결책: Failed Event 저장 및 배치 재처리
@KafkaListener(topics = "coupon_create", groupId = "group_1")
public void listener(Long userId) {
try {
couponRepository.save(new Coupon(userId));
} catch (Exception e) {
logger.error("failed to save coupon: "+ userId);
failedEventRepository.save(new FailedEvent(userId));
}
}
POST /api/coupons/apply
Content-Type: application/json
{
"userId": 1
}
{
"status": "success",
"message": "쿠폰 발급 신청이 완료되었습니다."
}
# 발급된 쿠폰 수 확인
redis-cli
> GET coupon_count
# 발급받은 사용자 목록 확인
> SMEMBERS applied_user
주요 테스트 케이스:
한번만응모()
: 기본 기능 테스트여러명응모_정합성_문제_해결_with_Redis()
: 동시성 해결 검증한명당_한개의_쿠폰발급()
: 중복 발급 방지 검증