개발 slecs

쿠폰 승인번호 중복 발급 버그 수정

목차

쿠폰 시스템에서 승인번호를 생성하는 방식을 클라이언트 방식에서 서버 채번으로 전환했다. 겉으로는 작은 fix이지만, 분산 시스템 환경에서 ID 생성 전략을 바꾸는 건 꽤 신중해야 하는 결정이다.

클라이언트 채번의 함정

원래는 클라이언트(또는 외부 연동 시스템)에서 ISSUE_APRV_SN을 생성해서 서버에 전달하는 방식을 썼다. 코드로는 단순해 보인다:

// Before: 클라이언트에서 ID 생성
const approvalSN = generateUUID();  // 외부 시스템에서 만든 
POST /coupon/issue { approvalSN, ... }

하지만 이런 방식은 실무에서 자주 터진다:

  • 중복 생성의 위험 — 네트워크 지연으로 재시도할 때, 같은 SN이 여러 번 들어올 수 있다
  • 동시성 제어 불가 — 서버가 받기 전에 클라이언트가 뭘 하는지 알 수 없음
  • 감시 불가 — 채번 로직이 분산되면 시스템 전체 흐름 추적이 어렵다
  • 외부 시스템 의존 — 클라이언트의 채번 규칙이 바뀌면 서버도 영향을 받음
  • 비즈니스 로직 노출 — "정확히 몇 번째 승인인가"를 클라이언트가 알 수 없음

쿠폰 승인번호는 단순 ID가 아니라 비즈니스 의미가 있는 값이다. 순차성, 유일성, 추적성이 중요하기 때문에 클라이언트에 맡기면 위험하다.

서버 채번으로의 전환

변경 후는 서버가 SN을 생성한다:

// After: 서버에서 ID 생성
POST /coupon/issue { ... }  // 클라이언트는 SN을 보내지 않음
RESPONSE { approvalSN, ... }  // 서버가 생성해서 응답

coupon/external/provider 패키지 안의 내부 클래스에서 이 로직을 관리한다. 외부 시스템과의 연동을 담당하는 영역이라, 이곳에 "서버가 채번한 SN을 외부에 알려주는" 책임을 집중시킨 것이다.

채번 방식 선택의 일반론

이런 변경은 아키텍처 수준의 결정이다. 팀 내에서는 항상:

언제 클라이언트 채번을 쓰나:
- 클라이언트가 로컬 스토리지에만 쓰는 임시 ID (draft, tempId)
- 서버와 충돌할 여지가 없는 경우 (예: 클라이언트 세션 ID)
- 네트워크 왕복을 피해야 하는 극도의 성능 중심 시나리오

언제 서버 채번을 해야 하나:
- 비즈니스 의미가 있는 ID (주문번호, 거래번호, 승인번호)
- 중복이 절대 안 되는 경우
- 감시/감사 기록이 필요한 경우
- 여러 클라이언트가 접근하는 자원

쿠폰 승인번호는 명백히 뒤쪽이다. 한 번 발급되면 취소될 때까지 그 ID로 추적되고, 정산에도 영향을 미친다. 중복이 나면 고객이 같은 번호로 두 번 쿠폰을 받을 수 있다.

구현 관점

파일 변경이 coupon/external/provider 영역으로 제한된 건 좋은 설계다. 쿠폰 발급 흐름 전체를 고쳐야 하는 게 아니라, 외부와의 인터페이스(provider)에서만 채번 로직을 끌어안은 것이다. 덕분에:

  • 내부 모듈은 "SN은 서버가 만든다"는 계약만 신경 쓰면 됨
  • 외부 시스템 변경에 대응하는 책임이 명확함
  • 나중에 채번 전략을 바꾸더라도 이 클래스만 수정하면 됨

이건 단순한 버그 수정이 아니라 책임 경계를 명확히 하는 리팩토링이다.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.