Tiny Star

프로젝트/쿠폰

[사용자 쿠폰] 기획

하얀지 2025. 8. 14. 13:58

 

 

아직 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건·처리 속도 측정

 

 

 

top