Files
llm-wiki/wiki/concepts/idempotency-key-design.md

11 KiB
Raw Permalink Blame History

title, source_type, status, confidence, tags, related_projects, last_reviewed
title source_type status confidence tags related_projects last_reviewed
Idempotency Key 설계 (triple scope vs Stripe/Square/Toss) llm-generated draft medium
idempotency
api-design
distributed-systems
ca-skeleton
2026-05-22

Idempotency Key 설계 (triple scope vs Stripe/Square/Toss)

Layer: wiki/concepts/ — 일반 개념. 내 프로젝트 사실은 raw/branch-notes/feature-rate-limit-idempotency-contract / raw/project-notes/ca-skeleton-operational-contract §29 Topic 5 참조.

Summary

Idempotency key는 동일한 mutating request의 재시도를 서버가 인식하도록 클라이언트가 생성하는 고유 값입니다. ca-tmpl은 key shape를 (authenticatedPrincipal, idempotencyKey, useCaseName) triple + DB table + 24h TTL + 200ms in-flight wait + fingerprint mismatch 시 HTTP 422로 정의합니다. 이 설계는 (a) triple scope로 endpoint dimension을 명시해 cross-use-case 충돌을 방지하고, (b) 24h TTL로 스토리지·키 추측 공격면을 최소화하며, (c) 200ms wait로 IETF draft의 즉시 409보다 retry 친화적인 hybrid를 채택하고, (d) body fingerprint mismatch를 409(in-flight)와 분리해 422로 표현한 점이 특징입니다.

Standard (공식 정의)

IETF draft (draft-ietf-httpapi-idempotency-key-header, draft-07, 2025-10)

  • Idempotency-Key HTTP request header를 정의 — Stripe / PayPal / Square / Adyen이 공통 참조하는 사실상의 헤더 표준 초안 (정식 RFC 아님).
  • 인용: "Uniqueness of the key MUST be defined by the resource owner and MUST be implemented by the clients." — key scope 정의는 resource owner의 책임으로 위임.
  • 인용: "If there is an attempt to reuse an idempotency key with a different request payload, the resource SHOULD reply with a HTTP 422 status code."
  • 인용: "The request was retried before the original request completed. The resource SHOULD respond with a resource conflict error" (HTTP 409).
  • TTL은 시간을 명시하지 않고 "정책을 정해 문서화하라"만 강제.

Stripe v1 pair → v2 triple

  • v1: (account, Idempotency-Key) pair. TTL 24h minimum. 5xx 응답까지 그대로 replay됨(결정적 응답).
  • v2: "idempotent request replay occurs when requests use the same idempotency key, are made to the same API, occur within the scope of the same account or sandbox, and occur within 30 days of each other."(account/sandbox, API, key) triple. TTL 30일.
  • fingerprint mismatch: "The idempotency layer compares incoming parameters to those of the original request and errors if they're not the same." (status code는 명시 안 함).

Square (Common API patterns)

  • idempotency_keybody 필드로 받음 (header 표준 미준수). endpoint별 dedup → (merchant_account, endpoint, idempotency_key) 사실상 triple.
  • fingerprint mismatch: "If you use the same idempotency key but change the CreatePayment request ... you get an error indicating that you used the idempotency key previously."
  • TTL 미공개, in-flight 동작 미정의.
  • 특수 디자인: cancel-payment-by-idempotency-key — 키 자체를 resource handle로 사용.

PayPal (Idempotency-Replay / PayPal-Request-Id)

  • header 이름이 Idempotency-Key가 아닌 PayPal-Request-Id (Stripe·IETF와 다름).
  • scope: (request-id, API call type). TTL 45일 — 조사된 reference 중 최장.

Toss Payments (기술블로그)

  • 4-tuple (account, key, URL, method) + TTL 15일. ca-tmpl보다 dimension 1개 많고 TTL 더 김.
  • header 이름은 Idempotency-Key로 IETF/Stripe와 동일.

AWS Lambda Powertools (idempotency utility)

  • key를 server-derived content-hash (function_name, payload_hash)로 도출 → 클라이언트가 header를 보낼 필요 없음.
  • 동일 payload면 동일 hash → 자동 dedup. body 변경 = 서로 다른 operation으로 취급.

GitHub REST API

  • API-level idempotency dedup을 제공하지 않음. 클라이언트 측 retry 정책에만 의존.

Brandur (Stripe 엔지니어 글) — Postgres locked_at lock

  • Postgres 테이블 + atomic phase 모델 + locked_at column으로 in-flight를 표현. abandoned key 회수는 별도 정책 필요.
  • Stripe 내부 구현의 가장 자세한 reference 문서.

한계 / 주의점

옵션 한계 / 주의점
Stripe v1 pair (account, key) endpoint dimension 부재 → API 추가 시 같은 키가 의도하지 않은 use case에 재사용될 위험. v2에서 API dimension 추가로 직접 보강.
Stripe v2 triple (account, API, key) IETF "resource owner가 정의" 범위 내에서 가장 엄격한 reference. TTL 30일은 보안 surface와 비용에 부담.
Square endpoint-scoped (body field) header 표준 미준수 → 미들웨어/게이트웨이 레벨에서 dedup 불가. URL path 변경 시 endpoint dimension 매핑이 깨질 수 있음. TTL 미공개로 클라이언트가 retry window를 가늠 못 함.
PayPal 45일 TTL 스토리지 비용 크고 키 추측 공격면이 가장 넓음. header 이름이 표준과 달라 멀티 PG 통합 비용 발생.
Toss 4-tuple (account, key, URL, method) URL/method가 scope에 들어가 HTTP path 변경(예: /v1/payments/v2/payments) 시 같은 의미의 재시도가 다른 키로 인식. version migration에 취약.
AWS Powertools content-hash 클라이언트가 키를 누락해도 동작하는 장점이 있으나, body의 사소한 변경(여백/필드 순서)이 다른 operation으로 분류 — JSON canonicalization 정책 필수.
Brandur Postgres lock (locked_at) locked_at만으로는 process crash 후 stale lock이 남을 수 있음 → abandoned key 회수(timeout-based release) 정책이 별도로 필요.
IETF draft 자체 draft 단계로 정식 RFC 아님. TTL / 저장 layer / lock 정책 등 운영 핵심을 표준이 다루지 않아 구현체별 동작이 제각각.
No API-level dedup (GitHub) 인프라/미들웨어 부담은 없으나 클라이언트가 모든 중복 위험을 책임 → 결제·금융 도메인에는 부적합.

흔한 오해

  • "Stripe pair보다 ca-tmpl이 무조건 안전" — v1 한정 비교. Stripe v2 triple과는 사실상 동등.
  • "IETF draft 422는 fingerprint mismatch의 표준" — draft는 SHOULD이지 MUST 아님. 구현체별로 400/409/422가 혼재.
  • "TTL은 길수록 안전하다" — 길수록 클라이언트 retry window는 늘지만 스토리지 비용과 키 추측 공격면도 함께 증가.

Project Application

Interview Questions

  • Q1. useCaseName (또는 endpoint) dimension을 scope에 포함시키는 이유는? Stripe v1 pair에서 어떤 충돌이 발생할 수 있는가?
  • Q2. TTL을 24h로 잡은 trade-off는? PayPal 45일·Stripe v2 30일과 비교했을 때 어떤 비용·위험을 줄이고, 어떤 use case(예: 결제·송금 long-running)에서는 부족한가?
  • Q3. 동시 도착 요청에 대해 200ms wait를 둔 의미는? IETF draft의 즉시 409와 비교했을 때 client retry 동작이 어떻게 달라지는가?
  • Q4. 같은 key + 다른 body를 422로, in-flight 충돌을 409로 분리한 이유는? 두 상황을 같은 코드로 합치면 어떤 클라이언트 버그가 가려지는가?
  • Q5. key가 클라이언트 생성 unique value라면 추측 공격면은 어떻게 평가해야 하는가? TTL이 길수록 공격면이 어떻게 변하고, AWS Powertools content-hash 방식은 이 문제를 어떻게 우회하는가?

Do Not Overclaim

  • "ca-tmpl triple이 Stripe pair보다 무조건 안전하다"고 말하지 않습니다. v1 pair 한정 비교이며, Stripe v2 triple과는 사실상 동급.
  • "ca-tmpl이 IETF Idempotency-Key spec을 완전히 준수한다"고 단정하지 않습니다. draft 단계이고, 422 fingerprint mismatch는 SHOULD이며, ca-tmpl의 200ms wait는 draft의 "즉시 409" 권고와 다른 선택입니다.
  • "Square가 표준 미준수라서 열등하다"고 단정하지 않습니다. body 필드 방식은 cancel-by-idempotency-key처럼 키를 resource handle로 쓰는 API 디자인의 장점이 있습니다.
  • "AWS Powertools content-hash가 header 방식의 상위 호환"이라고 말하지 않습니다. body의 사소한 변경(여백/필드 순서/timestamp)이 다른 operation으로 분류되므로 canonicalization 정책이 함께 가야 동작합니다.
  • "Brandur lock 패턴을 그대로 채택했다"고 말하지 않습니다. ca-tmpl은 200ms wait + unique constraint hybrid이며 Brandur locked_at lock의 변형입니다.
  • ca-tmpl 24h TTL이 "업계 표준"이라고 표현하지 않습니다. Stripe v1 최소값과 일치할 뿐이고, 다른 도메인 reference는 모두 더 길게 잡습니다.

Sources

공식 / 표준

구현 reference

raw 보존본

canonical 참조