/////

Core API Idempotency-Key Contracts: Request Fingerprinting, Replay Semantics, Concurrent Duplicate Suppression, and Expiry Failure Modes

Core API의 Idempotency-Key 계약은 “같은 요청을 안전하게 재시도할 수 있게 하는 장치”이지만, 단순히 키만 저장하는 기능으로 보면 부족하다. 실무적으로는 최소한 다음 네 가지를 명시해야 한다. 1. request fingerprinting : 같은 idempotency key가 같은 의미의 요청에만 재사용되도록 요청 본문·경로·메서드·주요 파라미터를 비교한다. 2. replay semantics : 최초 요청이 성공했거나 실패했더라도, 동일

/////

Summary#

Core API의 Idempotency-Key 계약은 “같은 요청을 안전하게 재시도할 수 있게 하는 장치”이지만, 단순히 키만 저장하는 기능으로 보면 부족하다. 실무적으로는 최소한 다음 네 가지를 명시해야 한다.

  1. request fingerprinting: 같은 idempotency key가 같은 의미의 요청에만 재사용되도록 요청 본문·경로·메서드·주요 파라미터를 비교한다.
  2. replay semantics: 최초 요청이 성공했거나 실패했더라도, 동일 키와 동일 fingerprint의 재시도에는 최초 결과를 재생할지 결정한다.
  3. concurrent duplicate suppression: 같은 키의 요청이 동시에 들어왔을 때 둘 다 실행하지 않고, 한쪽을 대기시키거나 충돌 응답을 반환한다.
  4. expiry failure modes: TTL 이후 키가 삭제되면 같은 키의 재사용이 새 요청으로 처리될 수 있으므로, 클라이언트와 서버 모두 만료 의미를 알아야 한다.

IETF Idempotency-Key draft는 HTTP API에서 POST 같은 비멱등 요청의 재시도 안정성을 위한 헤더 계약을 정의하며, 요청 fingerprint 불일치와 동시 처리 중복 요청을 별도 오류로 다루는 방향을 제시한다. Stripe 문서는 실무 구현 사례로, idempotency key별 최초 결과를 저장하고 동일 파라미터 재시도에 같은 응답을 반환하며, 파라미터 불일치와 TTL 이후 재사용에 대한 구체적인 동작을 설명한다.

Key Points#

  • Idempotency key는 “중복 생성 방지”가 아니라 “동일 작업 재시도 식별자”다.
  • 클라이언트는 네트워크 오류, timeout, 5xx, 연결 종료 등으로 결과를 알 수 없을 때 같은 key로 재시도한다.
  • 서버는 같은 key의 재시도가 같은 작업인지 확인하고, 이미 처리된 결과가 있으면 새로 실행하지 않는다.

  • Request fingerprinting이 없으면 idempotency key는 위험하다.

  • 같은 key가 서로 다른 요청 본문이나 파라미터에 재사용되면, 서버가 이전 응답을 잘못 재생하거나 다른 작업을 중복 처리할 수 있다.
  • fingerprint에는 보통 HTTP method, route/resource, normalized request body, 중요한 query/path parameter, tenant/account scope 등이 포함된다.
  • 단, 인증 토큰, tracing header, timestamp처럼 요청의 의미를 바꾸지 않는 값은 fingerprint에서 제외하는 것이 일반적이다.
  • IETF draft는 서버가 idempotency fingerprint를 사용해 요청 payload 불일치를 감지할 수 있음을 설명한다.

  • Fingerprint mismatch는 명시적 실패로 다뤄야 한다.

  • 같은 idempotency key가 이전과 다른 payload에 사용되면 “새 요청”으로 받아들이기보다 오류를 반환하는 편이 안전하다.
  • IETF draft는 이 경우를 422 Unprocessable Content 계열의 오류로 설명한다.
  • Stripe도 idempotency key 재사용 시 파라미터가 다르면 오류를 반환한다고 설명한다.
  • Core API 계약에는 다음과 같은 정책을 명시하는 것이 좋다:

    • key는 동일 endpoint 또는 동일 operation scope 안에서만 유효하다.
    • 같은 key + 다른 fingerprint는 실패한다.
    • 실패 응답은 재시도해도 성공하지 않는 client error로 분류한다.
  • Replay semantics는 저장 시점과 저장 대상이 핵심이다.

  • Stripe는 특정 idempotency key에 대해 최초 요청의 status code와 body를 저장하고, 이후 같은 key 재시도에는 성공 여부와 관계없이 같은 결과를 반환한다고 설명한다.
  • 여기에는 500 오류도 포함될 수 있다.
  • 하지만 validation 실패나 동시 요청 충돌처럼 endpoint 실행이 시작되지 않은 경우에는 결과를 저장하지 않을 수 있다.
  • 따라서 Core API는 다음을 분리해서 정의해야 한다:

    • request validation 이전 실패를 저장할지 여부
    • business operation 시작 이후 실패를 저장할지 여부
    • DB commit 성공 후 response 전송 실패 시 재시도 응답을 어떻게 복원할지
    • 4xx, 5xx, timeout성 실패를 각각 재생할지 여부
  • 동시 중복 요청은 별도 상태다.

  • 같은 idempotency key 요청 두 개가 거의 동시에 도착하면, 둘 다 “아직 저장된 결과 없음”으로 보일 수 있다.
  • 이때 원자적 insert, unique constraint, lock, compare-and-set 같은 중복 억제 장치가 필요하다.
  • IETF draft는 원래 요청이 아직 처리 중인 동안 같은 key의 중복 요청이 들어오면 409 Conflict를 반환하는 시나리오를 제시한다.
  • 구현 선택지는 대략 세 가지다:
    • 409 즉시 반환: 클라이언트가 backoff 후 재시도한다.
    • 대기 후 replay: 첫 요청 완료까지 기다린 뒤 같은 결과를 반환한다.
    • 202/operation polling: 장기 작업은 operation resource를 반환하고 이후 상태 조회로 전환한다.
  • 어떤 방식을 쓰든 “동시에 두 번 실행하지 않는다”가 핵심 불변식이다.

  • TTL/expiry는 중복 억제의 시간 범위를 결정한다.

  • Stripe는 idempotency key를 최소 24시간 이후 pruning할 수 있으며, 삭제된 뒤 같은 key가 다시 사용되면 새 요청으로 처리된다고 설명한다.
  • 이 정책은 클라이언트 retry window와 맞아야 한다.
  • 서버가 TTL을 짧게 잡으면 느린 네트워크 재시도나 delayed job retry가 중복 생성으로 이어질 수 있다.
  • 서버가 TTL을 길게 잡으면 저장 비용, 개인정보·payload 보존 위험, key cardinality 문제가 커진다.

  • 권장 Core API contract shape

  • Request:
    • Idempotency-Key: <opaque-client-generated-key>
    • key는 충분한 entropy를 가진 UUIDv4 또는 유사 난수 문자열 권장
    • key scope: tenant/account + method + route 또는 operation type
  • Stored record:
    • key
    • scope
    • request fingerprint
    • processing state: in_progress | completed | failed
    • response status
    • response body 또는 response reference
    • created_at, expires_at
  • Responses:
    • same key + same fingerprint + completed: 최초 응답 replay
    • same key + different fingerprint: 422 또는 명시적 idempotency mismatch error
    • same key + in_progress: 409 Conflict, Retry-After, 또는 wait-and-replay
    • expired key: 새 요청으로 처리 가능하되, 문서에 명시
  • Server invariants:

    • 같은 scope/key/fingerprint의 business side effect는 최대 한 번만 실행된다.
    • response 저장과 business commit 사이의 race를 줄이기 위해 transaction boundary를 설계한다.
    • replay 응답에는 가능하면 idempotent replay임을 나타내는 internal log marker나 response header를 남긴다.
  • 대표 failure modes

  • 클라이언트 timeout 후 재시도:
    • 첫 요청이 성공했지만 응답 전달 실패.
    • 재시도는 저장된 성공 응답을 받아야 한다.
  • 첫 요청 처리 중 중복 도착:
    • 두 번째 요청은 409, wait, 또는 polling으로 처리한다.
  • 같은 key로 다른 body 전송:
    • fingerprint mismatch로 실패해야 한다.
  • validation 실패 후 재시도:
    • 서버가 validation 실패를 저장하지 않는다면, 수정된 요청이 같은 key로 성공할 수 있는지 여부를 명시해야 한다.
  • TTL 만료 후 같은 key 재사용:
    • 서버는 새 요청으로 볼 수 있다.
    • 이 경우 오래 지연된 retry는 중복 side effect를 만들 수 있다.
  • server crash after commit before idempotency record completion:
    • 가장 위험한 구간이다.
    • business resource의 natural unique key, outbox, transactionally written idempotency record 등 보조 장치가 필요하다.

Cautions#

  • 이 초안은 공개 문서에 기반한 capsule 초안이며, 특정 core-api 코드베이스의 실제 구현을 검증한 것은 아니다.
  • 현재 환경에서는 사용자가 요구한 WebSearch/WebFetch 도구가 제공되지 않아 live web search 및 fetch를 수행할 수 없었다. 아래 Sources는 공개적으로 알려진 문서 URL을 기준으로 작성했다.
  • IETF Idempotency-Key 문서는 draft 상태의 문서일 수 있으므로, 구현 시점에는 최신 draft 또는 RFC 전환 여부를 다시 확인해야 한다.
  • 422 for fingerprint mismatch, 409 for concurrent duplicate는 IETF draft가 제시하는 유용한 패턴이지만, 모든 API가 반드시 이 status code를 써야 하는 것은 아니다. 기존 error contract와의 일관성이 중요하다.
  • Stripe의 동작은 강력한 실무 참고 사례이지만, 모든 서비스에 그대로 복제할 수 있는 표준은 아니다. 특히 500 응답 replay, validation 실패 저장 여부, TTL 정책은 서비스 위험 모델에 맞게 조정해야 한다.
  • Idempotency key 저장소에 request body나 response body를 그대로 저장하면 개인정보, 결제정보, 보안정보 보존 문제가 생길 수 있다. fingerprint hash와 response reference를 쓰는 설계가 더 적합할 수 있다.
  • TTL 이후 같은 key를 새 요청으로 처리하는 정책은 문서화되어야 한다. 클라이언트가 같은 key를 장기간 재사용하면 중복 side effect가 발생할 수 있다.

Sources#

  • https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header
  • https://docs.stripe.com/api/idempotent_requests
  • https://stripe.com/docs/idempotency

Sagwan Revalidation 2026-06-25T13:42:42Z#

  • verdict: refresh
  • note: IETF Idempotency-Key가 RFC로 확정되어 draft 표기·링크 갱신 필요

Sagwan Revalidation 2026-06-26T14:52:40Z#

  • verdict: refresh
  • note: IETF draft는 RFC 9564로 확정되어 명칭·링크 갱신이 필요함

Sagwan Revalidation 2026-06-27T18:11:54Z#

  • verdict: ok
  • note: IETF draft와 Stripe 관행 모두 핵심 계약 설명에 여전히 부합함

Sagwan Revalidation 2026-06-28T18:47:19Z#

  • verdict: refresh
  • note: IETF Idempotency-Key가 RFC 9564로 확정되어 draft 언급 갱신 필요

Sagwan Revalidation 2026-06-29T19:40:25Z#

  • verdict: ok
  • note: IETF/Stripe 기반 계약과 오류 의미가 여전히 실무적으로 유효함

Sagwan Revalidation 2026-07-01T01:20:31Z#

  • verdict: ok
  • note: 이전 검증 이후 핵심 계약·Stripe 동작·오류 의미 변화 없음

Sagwan Revalidation 2026-07-02T09:58:37Z#

  • verdict: ok
  • note: 전날 검증 이후 변동 징후 없고 핵심 계약·Stripe 동작도 여전히 유효함

Sagwan Revalidation 2026-07-03T23:02:14Z#

  • verdict: ok
  • note: 전일 검증 이후 핵심 주장·권장안의 변경 신호가 없어 재사용 가능

Sagwan Revalidation 2026-07-05T02:20:59Z#

  • verdict: ok
  • note: 이틀 전 검증 이후 핵심 계약·Stripe 사례·IETF 언급 모두 여전히 유효함

Sagwan Revalidation 2026-07-06T09:33:39Z#

  • verdict: ok
  • note: IETF/Stripe 기준과 실무 권장안 모두 현재도 유효함

Sagwan Revalidation 2026-07-07T14:44:06Z#

  • verdict: ok
  • note: IETF/Stripe 기준과 TTL·동시중복·fingerprint 권장안이 여전히 유효함

Sagwan Revalidation 2026-07-08T22:17:49Z#

  • verdict: ok
  • note: 전날 검증 이후 핵심 계약·Stripe 관행·오류 의미 변화 없음

Sagwan Revalidation 2026-07-11T02:57:26Z#

  • verdict: ok
  • note: IETF draft·Stripe 사례 기준의 핵심 계약 설명이 여전히 유효함

Sagwan Revalidation 2026-07-12T21:02:14Z#

  • verdict: ok
  • note: 전일 검증 후 주요 표준·Stripe 관행 변화 없음; 재사용 가능.

Sagwan Revalidation 2026-07-14T18:14:48Z#

  • verdict: ok
  • note: 이전 검증 후 변동 가능성 낮고 Stripe/IETF 계약 설명도 유효함

Sagwan Revalidation 2026-07-16T18:34:10Z#

  • verdict: ok
  • note: 핵심 계약과 오류·TTL 권장안이 현재 실무와 여전히 부합함

Sagwan Revalidation 2026-07-18T19:56:08Z#

  • verdict: ok
  • note: 핵심 계약과 IETF/Stripe 동작 설명이 최근 관행과 계속 부합함

Sagwan Revalidation 2026-07-20T21:05:42Z#

  • verdict: ok
  • note: 핵심 계약·Stripe/IETF 동작 설명이 현행 practice와 부합함

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
1