결제 웹훅 중복 처리를 멱등성 락으로 차단
목차
결제대행사 Webhook이 같은 건을 두 번 때리는 문제
운영 중 결제대행사에서 같은 결제 건에 대해 동일한 웹훅이 두 번, 세 번 들어오는 케이스가 누적됨. 첫 호출에서 정상 처리됐는데 두 번째 호출이 잔액을 한 번 더 건드리거나 알림이 중복 발송되는 사고가 발생했음.
원인을 정리하면 이런 흐름이었음.
- 결제대행사가 응답 ACK를 못 받으면 일정 간격으로 재시도
- 우리 쪽 응답이 지연되거나 5xx로 떨어진 이력이 있었음
- 게다가 일부 응답에서 거래키가 빈 값으로 오는 케이스가 섞여 있었음
멱등성 키 + 짧은 락으로 정리
처음엔 단순하게 "이미 처리된 상태면 무시"로 막아보려 했음. 근데 동시 진입을 못 잡음. 두 콜백이 거의 동시에 들어오면 둘 다 "미처리" 상태를 보고 둘 다 처리로 진입함.
결국 멱등성 키 + 짧은 락 패턴으로 다시 짰음.
| 단계 | 처리 |
|---|---|
| 1 | 거래키 + 이벤트 타입으로 멱등 키 생성 |
| 2 | 락 획득 실패 시 즉시 200 OK 응답 |
| 3 | 락 획득 성공 시 상태 검증 후 진입 |
| 4 | 처리 완료 후 결과를 짧은 TTL로 캐시 |
key = txKey + ":" + eventType
// SETNX, TTL 5분
200을 빨리 돌려주는 게 핵심이었음. 외부 입장에서 "받았다"고 인지해야 재시도가 멈춤. 처리 실패가 아닌 "중복"은 굳이 5xx로 응답할 이유가 없음.
거래키가 비어 오는 케이스 보정
빈 거래키 케이스가 진짜 골치였음. 파트너 응답 페이지에서 결과를 부모창으로 전달하는 JS가 있는데, 일부 흐름에서 거래키 필드가 누락된 채로 넘어왔음.
- 부모창 전달 스크립트에서 거래키를 명시적으로 포함하도록 수정
- 서버 측에선 거래키가 없을 때 주문번호 + 승인번호 조합으로 역추적해 보정
- 보정 후에도 식별 불가하면 별도 큐로 격리해 수동 검수
- 회원 조회 SQL에 보정 컬럼 인덱스 한 줄 추가, 풀스캔 위험 차단
보정 로직을 컨트롤러가 아니라 유틸 레이어로 끌어내린 게 만족스러움. 같은 보정이 나중에 다른 채널 웹훅에서도 필요해질 거 같았는데, 실제로 다음 스프린트에서 재활용했음.
배운 점
- 외부 콜백은 "한 번만 온다"는 가정 자체를 깔면 안 됨
- ACK 응답이 빠르고 정확해야 재시도 폭주를 안 부름
- 식별자 누락은 클라이언트와 서버 양쪽에서 막아야 안전함
- 보정 로직은 컨트롤러 말고 별도 유틸로 빼두면 재사용이 쉬움
다음
댓글 0
첫 댓글 달아줘.