7.4 KiB
title, source_type, status, confidence, tags, related_projects, last_reviewed
| title | source_type | status | confidence | tags | related_projects | last_reviewed | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ca-tmpl - Idempotency Key 결정 (triple scope + 24h TTL) | project | verified | high |
|
|
2026-07-02 |
ca-tmpl - Idempotency Key 결정 (triple scope + 24h TTL)
Layer:
wiki/projects/— 내 프로젝트 사실. 일반 개념은 wiki/concepts/idempotency-key-design 참조.
프로젝트 컨텍스트
ca-tmpl skeleton 프로젝트의 API contract 설계 트랙 중 하나로 진행한 idempotency key 정책 결정입니다. 다음 형태를 contract 문서에 명시했습니다.
- key shape:
(authenticatedPrincipal, idempotencyKey, useCaseName)triple scope - 저장소: DB table (Redis/in-memory가 아님)
- TTL: 24h
- 동시 도착 시: 200ms in-flight wait → 그래도 in-flight면 HTTP
409 - 같은 key + 다른 body fingerprint: HTTP
422
현재 단계: C2 구현 + 로컬 검증 완료. 2026-07-02 기준 /home/donghyeon/workspace/ca-tmpl/src의 실제 코드와 ./gradlew check 결과를 대조했다. application-core executor, web helper/codec, RDBMS store, PostgreSQL unique constraint contract가 존재한다. 운영 배포 검증은 없다.
실제 구현 내용 (actually-implemented)
application-core에IdempotencyExecutor,IdempotencyStorePort,IdempotencyRecord,RequestFingerprint, mismatch/in-flight 예외가 구현되어 있다.adapter-web에IdempotencyKeySupport와JsonIdempotentResponseCodec이 있어 HTTP header/principal/use case scope와 저장 응답 codec을 연결한다.adapter-persistence-rdbms에IdempotencyStoreAdapter,IdempotencyRecordEntity,IdempotencyRecordJpaRepository,IdempotencyReaper가 구현되어 있다.adapter-persistence-postgresql의V1__idempotency_record.sql이 DB schema와 unique scope를 소유한다.app-bootstrap의IdempotencyConfig/IdempotencySettings가 store, executor, reaper 설정을 배선한다.
로컬/dev 검증 (locally-verified)
./gradlew check통과(2026-07-02,BUILD SUCCESSFUL, 114 tasks).IdempotencyExecutorTest,RequestFingerprintTest,IdempotencyScopeTest가 executor/mismatch/scope 동작을 검증한다.IdempotencyStoreAdapterTest,IdempotencyReaperTest가 RDBMS adapter와 TTL cleanup을 검증한다.IdempotencyKeySupportTest,IdempotencyExceptionMappingTest가 web boundary와 error envelope mapping을 검증한다.IdempotencyUniqueScopeContractTest가 PostgreSQL Testcontainers 기반으로 unique scope contract를 검증한다.
운영 검증 (prod-verified)
없음. 운영 환경에 배포된 적이 없습니다.
문서/계획만 존재 (documented-only / planned)
이 섹션은 구현된 contract의 정책 경계와 아직 과장하면 안 되는 부분을 분리한다.
Key shape / TTL / 저장소 (canonical §29 Topic 5)
(authenticatedPrincipal, idempotencyKey, useCaseName)triple로 endpoint dimension을 scope에 포함.- TTL 24h. Stripe v1 minimum과 동일하고, 조사한 reference 중 가장 짧은 축.
- 저장소는 DB table (Redis 단독 의존 회피). Brandur Postgres 패턴의 변형.
- in-flight 처리: 200ms wait 후에도 충돌이면
409. - body fingerprint mismatch:
422.
8종 reference 비교 후 triple 채택
- 검토 대안: Stripe v1 pair / Stripe v2 triple / Square body-field / PayPal
PayPal-Request-Id45일 / Toss 4-tuple 15일 / AWS Lambda Powertools content-hash / GitHub no-dedup / Brandur Postgres lock. - 채택 근거 (설계 시점):
- storage 비용 — 24h TTL이 PayPal 45일·Toss 15일·Stripe v2 30일 대비 가장 짧음.
- key 추측 공격면 — TTL 짧을수록 노출 window 감소.
- endpoint dimension 보강 — Stripe v1 pair의 cross-use-case 충돌 위험 회피.
- URL/method를 scope에서 제외해 (Toss 4-tuple과 달리) HTTP path version migration에 강함.
409 vs 422 응답 코드 분리
409 Conflict— 동일 key의 in-flight 충돌 (200ms wait 후에도 원본 미완료).422 Unprocessable Entity— 동일 key + 다른 body fingerprint (클라이언트 버그 신호).- IETF draft가 in-flight를
409로, fingerprint mismatch를422로 권고(SHOULD)한 라인을 ca-tmpl 응답 코드에 그대로 반영.
IETF draft의 in-flight 409, fingerprint mismatch 422 권고는 project policy와 구현에 반영되어 있다. 단 200ms wait 값은 부하 측정 기반 튜닝값이 아니라 ca-tmpl 기본 정책값이다.
면접에서 말할 수 있는 범위
- 자신 있게 답할 수 있음
- "왜
useCaseName을 scope에 넣었나" — Stripe v1 pair의 cross-use-case 충돌 회피. - "왜 TTL 24h인가" — storage 비용·공격면 vs long-running retry window의 trade-off, 짧은 쪽 선택 이유.
- "200ms wait의 의미" — 즉시
409로 끊지 않고 client retry 친화적으로 hybrid 처리한 이유. - "409 vs 422 분리 의도" — in-flight 충돌과 fingerprint mismatch가 클라이언트에게 다른 신호임을 코드로 구분.
- "왜
- 적당히 답할 수 있음
- "IETF Idempotency-Key draft와의 정합성" —
SHOULD라인은 따랐으나 draft 단계임을 명시.
- "IETF Idempotency-Key draft와의 정합성" —
- 답하면 안 됨 (모른다고 해야 함)
- "idempotency executor/storage/web helper를 구현했고 로컬 테스트로 검증했다" — 가능. 단 운영 배포 경험은 없음.
- "동시성 부하 테스트로 200ms wait 값을 튜닝했다" — ❌. 측정값 없음.
- "운영에서 422 / 409 비율이 어땠다" — ❌. 운영 배포 자체가 없음.
과장 금지 지점
- "ca-tmpl triple이 Stripe pair보다 무조건 안전" — ❌. v1 pair 한정 비교. Stripe v2 triple과는 사실상 동급.
- "IETF Idempotency-Key spec을 완전히 준수한다" — ❌. draft 단계이며, 200ms wait는 draft의 "즉시 409" 권고와 deviation.
- "24h TTL이 업계 표준" — ❌. Stripe v1 최소값과 일치할 뿐, 다른 reference는 모두 더 김.
- "8종을 벤치마크해 채택했다" — ❌. 문서 비교이지 측정 비교가 아님.
- "구현했다 / 로컬 테스트로 검증했다"는 가능. "운영에서 검증했다 / 부하로 튜닝했다"는 금지.
Blog-topic ingest: application-layer idempotency executor (2026-07-02)
raw/blog-topics/idempotency-executor-application-layer-clean-architecture-2026-06-09 는 idempotency를 framework middleware가 아니라 application-layer executor와 storage port로 두고, rate-limit은 presentation interceptor가 소유하도록 분리한 글감이다.
- canonical 반영 범위: triple scope/TTL/409/422 결정 문서에 layer ownership 글감을 연결했다.
- blogify 전 조건: 충족. 이 문서는 2026-07-02 기준 코드와
./gradlew check로 검증됨. - 블로그 전 과장 방지: IETF draft의 즉시 409 권고와 ca-tmpl의 200ms wait deviation을 분리한다.
관련 개념
Sources
- raw/project-notes/ca-skeleton-operational-contract §29 Topic 5
- raw/branch-notes/feature-rate-limit-idempotency-contract
- raw/branch-notes/feature-api-contract-baseline
- raw/blog-topics/idempotency-executor-application-layer-clean-architecture-2026-06-09 — application-layer idempotency executor 블로그 글감 raw seed.