중복 결제 방지 설계 면접 질문: 같은 요청이 두 번 와도 한 번만 실행하려면
시스템 디자인 Vol.1 · 16장 「두 번 눌러도 한 번, 중복 요청」에서
이런 화면입니다
결제하기를 눌렀는데 화면이 한참 멈춰 있습니다. 불안해서 한 번 더 누릅니다. 잠시 뒤 결제 완료 문자가 두 통 옵니다.
느린 응답, 한 번 더 누른 손가락, 승인 문자 두 통. 결제를 다뤄 본 팀이라면 한 번은 겪는 사고입니다.
면접관이 이렇게 묻습니다
주문 결제 요청이 두 번 도착할 수 있습니다. 버튼을 두 번 누르기도 하고, 네트워크가 끊겨 클라이언트가 재시도하기도 합니다. 같은 결제가 두 번 실행되지 않게 설계해 보세요.
범위부터 좁힙니다
한 줄로 좁힙니다. 같은 결제 요청이 몇 번 도착하든 돈은 한 번만 나간다. 카드사 연동 세부는 접고, 우리 서버가 중복을 알아보는 데 집중합니다.
같은 결제 요청이 두 번 도착했을 때, 서버가 무엇을 보고 같은 요청이라고 알아볼지가 핵심 질문입니다.
가장 많이 나오는 답
결제 버튼을 누르면 비활성화합니다. 혹시 몰라 서버에서는 같은 사용자의 같은 금액이 5초 안에 또 오면 중복으로 보고 버립니다.
if (recent(userId, amount, Duration.ofSeconds(5))) {
throw new DuplicatePaymentException();
}
버튼을 연타하는 시연에서는 깔끔하게 막힙니다.
그런데 여기서 깨집니다
재시도와 새로고침은 버튼 바깥에서 일어납니다. 응답이 끊긴 앱이 같은 요청을 다시 보내면 버튼은 아무것도 막지 못합니다.
추측 로직은 반대쪽에서 깨집니다. 같은 커피를 정말 두 잔 연달아 시킨 손님의 두 번째 주문이 잘립니다. 중복이라며 에러를 던지면, 손님은 결제가 됐는지 안 됐는지 더 모르게 됩니다.
떨어지는 답은 이런 모양입니다
- 버튼 비활성화로 끝냅니다.
- 같은 금액이 짧은 시간 안에 오면 중복으로 추측합니다.
- 중복 요청에 에러를 던져, 결제가 됐는지 손님을 더 헷갈리게 합니다.
면접관은 여기서 멈추지 않습니다
첫 요청이 아직 처리 중인데 재시도가 도착하면요?
처리 중인 상태를 어떻게 다룰지 봅니다.
우리 앱 대신 제휴사 서버가 이 결제 API를 부르면요?
우리 앱이 아닌 호출자에게도 같은 방법이 통하는지 봅니다.
카드사로 보낸 요청이 타임아웃되면요? 승인 여부를 모르는 채로요.
모르는 상태를 모르는 상태로 정직하게 다루는지 봅니다.
이 다음 판단은 책에서 이어집니다
책은 같은 요청을 무엇으로 알아볼지 정하고, 두 번째 요청이 왔을 때 무엇을 돌려줄지, 그 장치를 운영할 때 빠지는 세 가지 함정까지 풉니다.
16장에서 이어지는 것
- 같은 요청을 알아보는 기준
- 두 번째 요청에 돌려줄 것
- 운영할 때 빠지는 세 가지 함정
- 이 장이 만든 테이블을 행으로 읽어 보기