결제 서비스에서 예를 들어, POST가 결제 승인에 쓰이는데 POST는 기본적으로 멱등하지 않다.

요청 실패인지 응답 유실인지 구분할 수 없다

클라이언트가 요청이 실패한 건지, 아니면 결제 이후 응답만 유실된 건지 구분할 수 없는데, 이걸 전부 결제 재시도로 처리해 버리면

sequenceDiagram
    participant C as 클라이언트
    participant S as 서버
    participant P as PG사
    C->>S: 결제 요청
    S->>P: 승인 요청
    P-->>S: 승인 성공
    S--xC: 응답 유실
    Note over C: 타임아웃 예외 수신
    C->>S: 같은 결제 재시도
    S->>P: 승인 요청
    P-->>S: 승인 성공
    Note over P: 중복 결제 발생

클라이언트 입장에서 두 상황 모두 타임아웃으로 예외를 똑같이 받기 때문에, 서버를 구현할 때 중복 요청을 스스로 걸러낼 수 있어야 한다는 것을 깨달음.

<aside> 💡

분산 시스템에서 필수라고 생각됨 — 멱등성

</aside>

이외에도 여러 중복 요청이 발생 가능한 지점이 있을 수 있음.

모든 결제에 멱등키를 설계했다

따라서 모든 결제에 대한 멱등키를 설계하여 해결하고자 하였음.

클라이언트가 만들게 되어있다. 결제 건에 대해 UUID 한번 생성하고, 재시작하면 다시 보내도록 함. HTTP 헤더에 담아서 보낸다.

실제 PG사(토스페이먼츠 등)에서 이런 방식으로 해결한다는 것을 기술 블로그 조사를 통해 알게 되어 도입함.

키의 유효 범위

주의 점은 키의 유효 범위다.

사용자 별로 유일해야 하므로 사용자 ID + 결제 건에 대한 멱등키를 복합 유니크로 잡았다. 같은 사용자, 같은 결제를 구분하고자 하였음. 보안 개선.

보관 기간 설정 (TTL)

보관 기간 설정(TTL)도 중요하다. 24시간으로 설정해두고 만기되도록 함.


Check then act 패턴 문제