9.9 KiB
title, source_type, url, archive_url, related_branches, related_projects, tags, created
| title | source_type | url | archive_url | related_branches | related_projects | tags | created | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| company-tech-blog / Brandur Leach — Implementing Stripe-like Idempotency Keys (Client-Generated Key vs Server-Assigned Resource ID) | company-tech-blog | https://brandur.org/idempotency-keys |
|
|
|
2026-05-31 |
Brandur Leach — Implementing Stripe-like Idempotency Keys (Client-Generated Key vs Server-Assigned Resource ID)
Layer:
raw/company-tech-blogs/— 전 Stripe 엔지니어 Brandur Leach 의 개인 기술 블로그. Stripe 내부 idempotency 구현 패턴을 일반화한 글. Stripe 공식 문서 아님 —engineering-blog등급 적용. best practice 단정 금지. 가장 널리 인용되는 idempotency key 구현 레퍼런스.이 파일의 초점:
feature-resource-identifier-contract의 D4 (ID 생성 책임) · D14 (Idempotency-Key vs Resource ID 구분) · D11 (Public ID vs Internal Sequence 분리) 결정 정당화. Postgres/DB 구현 상세(locked_at, atomic phase 등)는 raw/company-tech-blogs/idempotency-brandur-stripe-postgres 에 별도 보관.
Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| raw/branch-notes/feature-resource-identifier-contract | D4 (ID 생성 책임): Idempotency-Key 는 client-generated, resource ID 는 server-assigned — 두 책임의 원천이 다름을 원문으로 뒷받침. D14 (Idempotency-Key vs Resource ID): 명시적 분리 + 수명주기 차이 + 형식 무관 조합 허용. D11 (Public ID vs Internal Sequence): Stripe 가 external-only (단일 public ID) 패턴을 쓰고 idempotency key 를 별도 레이어로 두는 사례 |
출처 / Source
- 원본 URL: https://brandur.org/idempotency-keys
- 아카이브 URL: (미수집)
- 저자 / 조직: Brandur Leach (전 Stripe 엔지니어, 개인 기술 블로그 brandur.org)
- 발행일: 본문 명시 없음 (2017~2018 추정)
- 마지막 확인일: 2026-05-31
왜 저장했는지 / Why archived
Idempotency-Key 가 client-generated 임을 원문으로 확인하고, server-assigned resource ID 와의 명시적 분리를 feature-resource-identifier-contract (D4/D14/D11) 의 근거로 삼기 위해 보관. 기존 idempotency-brandur-stripe-postgres.md 가 DB/Postgres 구현에 초점을 두는 반면, 본 파일은 ID 생성 책임의 주체(client vs server) 와 수명주기 분리에 초점.
핵심 인용 / Key quotes (verbatim, 3~5개)
[§HTTP header example] "A common way to transmit an idempotency key is through an HTTP header:" (원문 코드 예시:
Idempotency-Key: 0ccb7813-e63d-4377-93c5-476cb93038f3) — line 4 in fetched text
[§Key definition] "An idempotency key is a unique value that's generated by a client and sent to an API along with a request." — line 12 in fetched text
[§Key format hint] "something with good randomness like a UUID" — line 14 in fetched text
[§Key TTL] "Keys are not meant to be used as a permanent request archive but rather as a mechanism for ensuring near-term correctness. Servers should recycle them out of the system beyond a horizon where they won't be of much use – say 24 hours or so." — line 16 in fetched text
[§Fingerprint / params mismatch] "Programs sending multiple requests with different parameters but the same idempotency key is a bug." — line 20 in fetched text
Claims Extracted / 추출된 주장
이 자료가 직접 말하는 것만 claim 으로 분리한다. Stripe 공식 문서 아님 —
engineering-blogstrength 이상으로 격상 금지.
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| BRANDUR-IDEMP-C8 | Idempotency-Key 는 client 가 생성해서 API 요청과 함께 전송하는 unique value 임 |
[§Key definition] "An idempotency key is a unique value that's generated by a client and sent to an API along with a request." | engineering-blog |
Idempotency-Key HTTP header 의 생성 책임이 client 에 있음 (D4 근거) | server 가 Idempotency-Key 를 생성하면 안 된다는 규범적 금지 규칙 (이 블로그는 권고 사례이지 표준이 아님) |
| BRANDUR-IDEMP-C9 | Idempotency-Key 의 포맷은 "UUID 처럼 난수성이 높은 것" 을 권장 | [§Key format hint] "something with good randomness like a UUID" | engineering-blog |
key 포맷 선택 가이드 (D4 / D14 보조) | UUID v4 만 허용된다는 뜻이 아님. ULID / NanoID 등 다른 포맷도 동등하게 사용 가능 |
| BRANDUR-IDEMP-C10 | Idempotency-Key 의 TTL 은 영구 보관이 아닌 단기 정확성 보장 용도이며, 24시간 정도가 적절 | [§Key TTL] "Keys are not meant to be used as a permanent request archive but rather as a mechanism for ensuring near-term correctness. Servers should recycle them out of the system beyond a horizon where they won't be of much use – say 24 hours or so." | engineering-blog |
idempotency key TTL 정책 설계 (D14 의 수명주기 차이 근거) | resource ID 의 수명주기 (persistent, 영구) 와의 차이를 명시적으로 비교하지는 않음 — 대조 추론은 wiki/concepts 에서 |
| BRANDUR-IDEMP-C11 | Idempotency-Key 는 HTTP header 로 전송하는 것이 일반적 패턴 | [§HTTP header example] "A common way to transmit an idempotency key is through an HTTP header" (코드 예시: Idempotency-Key: 0ccb7813-e63d-4377-93c5-476cb93038f3) |
engineering-blog |
HTTP API 에서 idempotency key 전달 방식 (D14 분리 근거) | 이것이 유일한 전송 방법이라는 뜻은 아님 (query param / body 전달도 기술적으로 가능) |
| BRANDUR-IDEMP-C12 | 동일 key + 다른 request params 요청은 client 측 버그로 명시 — 서버는 이를 거부해야 함 | [§Fingerprint / params mismatch] "Programs sending multiple requests with different parameters but the same idempotency key is a bug." | engineering-blog |
request fingerprint 비교 정책 (D14 보조 — idempotency key 와 request 내용의 결합 의미) | 거부 시 HTTP status code (409 vs 422) 는 이 인용에 없음 (Brandur 는 409 사용, IETF draft 는 422 권고) |
Strength 허용값 (참고)
engineering-blog— 개인/팀 블로그의 엔지니어링 해설 (본 자료의 등급)
Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
BRANDUR-IDEMP-C8: Idempotency-Key 가 client-generated 임 (D4 의 "Idempotency-Key 는 client 생성" 근거)BRANDUR-IDEMP-C9: UUID 같은 난수 포맷 권장 (D4/D14 포맷 가이드)BRANDUR-IDEMP-C10: Idempotency-Key TTL 이 단기(~24h)이고 영구 보관이 아님 (D14 의 수명주기 차이)BRANDUR-IDEMP-C11: Idempotency-Key 가 HTTP header 로 전달됨 — resource ID 는 response body / URL path 에 위치 (D14 분리의 물리적 근거)BRANDUR-IDEMP-C12: 같은 key + 다른 params = client bug — fingerprint 검사 의무 (D14 보조)
- 이 자료가 증명하지 않는 것:
- Stripe 의 resource ID 와 idempotency key 를 명시적으로 대조 한 서술은 없음 — 원문은 idempotency key 만 집중 서술. resource ID 의 분리는 구조적 추론.
- D11 (Public ID vs Internal Sequence): 원문은 Stripe 가 single public UUID 만 쓴다고 명시하지 않음 — Stripe 공식 API docs 로 보강 필요.
- HTTP status 409 vs 422 의 표준 적합성 — IETF draft 별도 확인 필요.
- 이 자료는
engineering-blog등급 — "Stripe 공식 best practice" 로 표현 금지.
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- ca-skeleton 의
Idempotency-Key24h TTL 이 Brandur 의 "~24 hours or so" 와 일치하는지 (ca-tmpl 결정에서는 24h 채택 — 이 인용이 direct support 가능). - Idempotency-Key 포맷 (UUID v4 권장) 과 resource ID 포맷 (ULID/UUID v7 — D1 결정) 의 다른 형식 조합 허용 여부 — 이 블로그는 조합에 제약을 두지 않음 (UNSUPPORTED_DECISION 여지 없음).
feature-resource-identifier-contractD11 (external-only vs dual) 에 대한 Stripe 사례 뒷받침은 Stripe 공식 API doc 별도 보강 권고.
- ca-skeleton 의
메모 / Notes
BRANDUR-IDEMP-C8~C12는 기존idempotency-brandur-stripe-postgres.md의C1~C7과 Claim ID 연번 충돌 없이 설계됨 (같은 PREFIX 의 다른 raw file 이므로 연번 구분 필요 — 이 파일의 claims 은 C8 부터).- D14 (Idempotency-Key vs Resource ID 구분) 에서 이 자료가 직접적인 대조 서술은 제공하지 않음. 그러나
C8(client-generated) +C10(TTL ~24h) +C11(HTTP header 전달) 을 조합하면 resource ID (server-assigned, persistent, URL path) 와의 대조 추론이 가능 — 이 추론은 wiki/concepts 또는 branch decision note 에서만 서술. - 포맷 조합 자유도 (
C9): idempotency key 는 UUID v4, resource ID 는 ULID 의 조합이 이 블로그의 내용과 충돌하지 않음. - Claim C8 이 D4 의 핵심 direct evidence. "client-generated" 한 단어가 ID 생성 책임 결정의 분기점.
Related / 관련
- 같은 URL 의 다른 초점 raw (DB/Postgres 구현):
- raw/company-tech-blogs/idempotency-brandur-stripe-postgres — locked_at, atomic phase, recovery point, reaper 72h, scope (user_id, key) 등 구현 상세 (C1~C7)
- 같은 주제 다른 raw:
- raw/company-tech-blogs/idempotency-toss-payments-techblog — Toss Payments 4-tuple scope + 15일 TTL + 409 in-flight
- raw/company-tech-blogs/idempotency-redis-vs-db-storage — Redis vs DB 저장소 trade-off
- 관련 branch:
- raw/branch-notes/feature-resource-identifier-contract — D4/D14/D11 결정 (이 자료의 primary consumer)
- raw/branch-notes/feature-rate-limit-idempotency-contract — Idempotency-Key 운영 계약 SSOT
- raw/branch-notes/feature-api-contract-baseline — fingerprint mismatch 응답 코드 정책
- 인용한 wiki 요약: (미작성)