Files
llm-wiki/vault/30-knowledge/projects/ca-tmpl/idempotency-key-design.md
T

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
ca-tmpl
idempotency
api-design
actually-implemented
locally-verified
ca-tmpl
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-coreIdempotencyExecutor, IdempotencyStorePort, IdempotencyRecord, RequestFingerprint, mismatch/in-flight 예외가 구현되어 있다.
  • adapter-webIdempotencyKeySupportJsonIdempotentResponseCodec이 있어 HTTP header/principal/use case scope와 저장 응답 codec을 연결한다.
  • adapter-persistence-rdbmsIdempotencyStoreAdapter, IdempotencyRecordEntity, IdempotencyRecordJpaRepository, IdempotencyReaper가 구현되어 있다.
  • adapter-persistence-postgresqlV1__idempotency_record.sql이 DB schema와 unique scope를 소유한다.
  • app-bootstrapIdempotencyConfig / 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-Id 45일 / 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 단계임을 명시.
  • 답하면 안 됨 (모른다고 해야 함)
    • "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

Cluster / 묶음