
아직 Redis에 익숙하지 않아서 이걸 사용한 쿠폰 서비스를 새롭게 만들어보려고 한다.
리디북스에서는 알림을 받은 사용자 외에는 참여하지 못하는 이벤트가 있고, 알라딘은 알림을 받은 사용자만 적립금을 받을 수 있는 이벤트(?)가 있다. 이렇게 구독중인 회원에게만 쿠폰을 발급하는 경우가 있고 여기에도 트래픽이 몰리기 때문에 이 아이디어를 가지고 작은 프로젝트를 기획했다.
기존에 했던 프로젝트에선 쿠폰 발급을 누르면 모든 사용자가 발급이 가능했지만, 이번에는 알림을 받은 사용자가 해당 알림으로 접속해야지만 쿠폰 발급이 가능하도록 만들 것이다.
목표
- 알림에 포함된 링크로 진입한 사용자만 쿠폰 발급 가능
- 링크 공유·재사용·위조 방지
- 동시성 높은 상황에서도 중복 발급 없이 빠르게 처리
핵심
- 서명 토큰: 알림 링크에 토큰을 포함. 토큰에는 `userId, eventId, jti(난수/고유번호), exp(만료)`를 담고 `SHAT256` 또는 `JWT` 사용.
- 토큰 → 세션 자격 변환: 사용자가 링크로 들어오면 서버가 토큰을 검증하고, 단 한 번만 사용 가능한 `eligible:eventId:jti` 세션 부여.
- 발급 제한: Redis 또는 DB로 확인 후 한 번만 쿠폰을 꺼내줌.
- 공유/재사용 막기: 토큰은 `userId`에 바인딩되고 `jti`는 사용 즉시 소진. 다른 계정/비로그인/만료/위조는 모두 거절
아키텍쳐
- View: JSP + Spring MVC
- Auth: Spring Security(세션)
- Broker: Redis Streams/Kafka로 알림 대량 발송 시뮬레이션
- Cache/Lock: Redis (토큰 사용표시, 쿠폰 재고 원자 발급)
- DB: MariaDB(MyBatis)
테이블
- 사용자
- `user` (id, email, status, created_at)
- 이벤트 정보
- `event` (id, name, starts_at, ends_at, is_active)
- 이벤트 유효성 검사(시작/종료/활성) 기준이 됨.
- 쿠폰
- `coupon_pool` (id, event_id, code, is_used, used_by, used_at)
- 발급 시 여기서 한 장을 꺼내 쓰고, 곧바로 사용 처리 플래그를 갱신.
- 쿠폰(토큰) 추적
- `notif_token` (id, jti, user_id, event_id, issued_at, clicked_at, consumed_at)
- 서버는 링크 진입 시 clicked_at 기록, 실제 발급 성공 시 consumed_at 기록.
- 쿠폰 발급 이력
- `coupon_issue` (id, user_id, event_id, code, jti, created_at, status)
플로우
- 관리자 발송 생성 → 대상자별 토큰 생성
- 서버가 (userId, eventId, jti, exp)로 서명토큰 발급
- 알림 URL: /events/{eventId}?t=<token>
- 사용자가 링크 클릭하여 이벤트 페이지 접속
- 컨트롤러가 토큰 검증: 서명, 만료, event 활성, userId 일치
- 비로그인 시 로그인 유도 후 동일 URL로 리다이렉트
- 검증 성공 시: Redis SETNX token:used:{jti} 0 EX <TTL> → 세션에 eligible:eventId:jti 저장, DB에 clicked_at 기록
- [발급] POST /events/{eventId}/claim
- 세션 플래그 확인(eligible) + CSRF 검증
- Redis 처리
- 키1: token:used:{jti}가 아직 0인지 확인 후 1로 바꿈
- 키2: coupon:stock:{eventId} 목록에서 LPOP로 쿠폰 코드 하나 꺼냄
- 성공 시 DB coupon_issue 기록, coupon_pool 업데이트
- 실패 케이스: 토큰 재사용, 재고 부족, 이벤트 비활성 → 409/410 응답
- 발급 여부 JSP에서 alert 창과 함께 이벤트 페이지 노출, 이후 재호출 막기
엔드포인트
- GET /events/{id}: 토큰 검증, eligible 세션 부여, 이벤트 페이지 렌더
- POST /events/{id}/claim: 쿠폰 발급 처리
- GET /events/{id}/result: 발급 결과 확인
테스트 시나리오
- 테스트 사용자 10만 명 더미 생성
- 이벤트 생성 + 쿠폰 10만 장 적재(쿠폰 풀은 Redis List에도 미리 적재)
- 토큰 생성 후 가상의 알림 발송: 실제 푸시는 생략하고, k6가 “알림 링크를 클릭한 것처럼” GET 요청을 보낸 뒤 곧바로 POST 발급을 수행
- 성공률·중복발급 0건·처리 속도 측정
'프로젝트 > 쿠폰' 카테고리의 다른 글
| [사용자 쿠폰] 이벤트 쿠폰 생성 (DB, Redis) (2) | 2025.08.22 |
|---|---|
| [쿠폰] 성능 테스트 및 개선 (JDBC Bulk Insert) (0) | 2025.04.30 |
| [쿠폰] 테스트를 위한 k6 + InfluxDB + Grafana 구성 (1) | 2025.04.30 |
| [쿠폰] 쿠폰 발급 수정 (saveAll, @Transactional) (0) | 2025.04.29 |
| [쿠폰] 쿠폰 발급 (Redis Stream, CustomDbWorker) (0) | 2025.04.29 |