실제로 작동하는 분산 시스템의 멱등성
중복된 사이드 이펙트 중단
분산 시스템에서의 멱등성(Idempotency)은 네트워크가 실패하거나, 큐가 재시도하거나, 클라이언트가 패닉에 빠지거나, 운영자가 재생(Replay) 버튼을 눌렀을 때 우리를 구해 주는 속성입니다. 프로덕션 시스템에서는 중복 전달(Duplicate Delivery)이 정상적인 현상이며, 중복된 부수 효과(Side Effect)가 발생하는 것이 버그입니다.
HTTP는 멱등 메서드를 서버에 대해 하나의 요청과 동일한 의도된 효과를 가지는 여러 개의 동일한 요청으로 정의합니다. 따라서 PUT, DELETE 및 안전(Safe) 메서드는 프로토콜 시맨틱상 멱등하며, 통신 장애 후 자동으로 재시도할 수 있습니다.

이 정의는 유용하지만 충분하지 않습니다. 실제 아키텍처에서 멱등성은 HTTP의 일반 상식 퀴즈 답이 아닙니다. 이는 비즈니스 보장입니다. 고객이 ‘결제’ 버튼을 한 번 눌렀다면, 커밋과 응답 사이의 타임아웃 때문에 두 번 청구해서는 안 됩니다. 워커가 재고량을 업데이트한 후 메시지 확인(Ack) 전에 충돌한다면, 브로커가 메시지를 재전송했기 때문에 재고량을 두 번 차감해서는 안 됩니다. 이것이 기준입니다.
제가 반복적으로 보는 실수는 멱등성을 전송 기능(Transport Feature)으로 취급하는 대신 시스템 속성(System Property)으로 취급하지 않는 것입니다. 큐의 중복 제거, HTTP 동사, 클라이언트 재시도는 도움이 되지만, 동일한 비즈니스 의도가 두 번째 부수 효과를 생성하도록 허용하는 설계는 구제할 수 없습니다. 이러한 통합 결정이 서비스 경계와 지속성 트레이드오프에 어떻게 부합하는지에 대한 더 넓은 프레임워크를 원한다면, 실제 애플리케이션 아키텍처: 통합 패턴, 코드 설계 및 데이터 접근를 먼저 참고하세요.
프로덕션에서 중복이 발생하는 곳
중복은 팀이 부주의해서 발생하는 것이 아닙니다. 분산 시스템이 재시도하고, 순서를 재배열하며, 재생하기 때문에 발생합니다.
클라이언트는 생성 요청을 보낼 수 있고, 서버는 이를 커밋할 수 있지만, 응답은 여전히 전송 중 사라질 수 있습니다. 이것이 바로 HTTP가 멱등 메서드를 구분하는 이유이며, Stripe와 PayPal과 같은 결제 API가 POST와 같은 안전하지 않은 메서드에 대해 명시적인 멱등성 메커니즘을 노출시키는 이유입니다.
메시지 브로커는 이 문제를 더욱 뚜렷하게 만듭니다. 최소 한 번 전달(At-least-once delivery)은 동일한 메시지에 대해 소비자가 반복적으로 호출될 수 있음을 의미하며, 핸들러는 데이터베이스를 성공적으로 업데이트했지만 확인(Acknowledgment) 전에 실패하여 브로커가 동일한 메시지를 다시 전달하게 만듭니다.
웹훅(Webhook)도 다르지 않습니다. GitHub는 웹훅 전달이 순서 없이 도착할 수 있으며, 실패한 전달은 자동으로 재전송되지 않고, 각 전달에는 재생 공격을 방지할 때 사용해야 하는 고유한 X-GitHub-Delivery GUID가 포함된다고 명시합니다. 채팅 엔드포인트를 상호작용 경계로 보는 실용적인 아키텍처 관점을 원한다면, 현대 시스템에서의 시스템 인터페이스로서의 채팅 플랫폼를 참조하세요.
강력한 보장을 광고하는 시스템조차도 여전히 여러분에게 작업을 남깁니다. Kafka는 멱등 프로듀서를 통해 Kafka 로그의 중복 항목을 방지할 수 있으며, 트랜잭션과 read_committed 소비자를 사용하여 Kafka 내부에 머무는 읽기-처리-쓰기 흐름에 대해 정확 한 번 전달(Exactly-once delivery)을 제공할 수 있습니다. 그러나 Kafka 자체 설계 문서도 외부 시스템은 오프셋과 출력과의 조정이 필요하다는 점을 명확히 하고 있습니다. Google Cloud Pub/Sub의 정확 한 번 전달은 풀 구독(Pull subscriptions)으로 제한되며, 클라우드 리전 내에서만 유효하고, 여전히 클라이언트가 확인이 성공할 때까지 처리 진행 상황을 추적해야 합니다.
제 의견이 담긴 요약은 간단합니다. 전송이 재시도될 것이라고 가정하세요. 운영자가 재생할 것이라고 가정하세요. 웹훅이 늦게 도착할 것이라고 가정하세요. 반복된 의도가 두 번째 비즈니스 효과를 생성할 수 없도록 쓰기 경로를 설계하세요. 오류 설계도 밀접하게 관련되어 있습니다: 오류가 어떻게 감싸지고, 번역되며, 재시도 가능(Retryable)한 것과 재시도 불가능한 것으로 분류되는지는 동일한 경계 규율의 일부입니다 — Go 오류 처리 아키텍처: 경계와 패턴에서는 재시도 가능 오류 분류, 경계 번역, 그리고 재시도 로직이 올바른 결정을 내릴 수 있게 하는 센티널 패턴을 다룹니다. 재시도가 비정상적인 종속성을 계속 타겟팅할 때, 통합 경계에서의 서킷 브레이커는 재시도 폭풍이 중복 작업을 증폭시키기 전에 빠르게 실패합니다.
실제로 신뢰하는 API 계약
멱등성 키는 중복 API 요청을 어떻게 방지합니까
변형 작업을 위한 유일한 API 계약은 호출자가 제공하는 의도(Caller-supplied intent)와 서버 측 지속성입니다.
AWS는 호출자가 제공하는 요청 식별자를 권장하고, 서비스는 멱등성 토큰을 변형 작업과 함께 원자적으로 기록해야 한다고 경고합니다. Stripe는 키에 대한 첫 번째 상태 코드와 응답 바디를 저장하고, 후속 매개변수를 원래 요청과 비교하며, 재시도에 대해 동일한 결과를 반환합니다. PayPal은 지원되는 POST API에 PayPal-Request-Id를 사용하며, 동일한 헤더가 있는 이전 요청에 대한 최신 상태를 반환합니다.
이로 인해 다음과 같은 실용적인 계약이 도출됩니다:
- 클라이언트는 비즈니스 작업에 대한 멱등성 키를 생성합니다.
- 서버는 해당 키를 테넌트(Tenant)와 작업 이름으로 스코핑합니다.
- 서버는 동일한 키가 다른 페이로드에 재사용되지 않도록 요청 해시를 저장합니다.
- 서버는
pending,completed, 또는failed와 같은 상태를 기록합니다. - 동일한 키를 사용한 재시도는 저장된 결과를 반환하거나 이에 대한 안정적인 포인터를 반환합니다.
- 동일한 키와 다른 페이로드를 사용한 재시도는 명확하게 실패합니다.
IETF Idempotency-Key 헤더 초안이 존재하지만, 2026-05-09 기준 IETF Datatracker에서는 발행된 RFC가 아닌 만료된 인터넷 초안(Expired Internet-Draft)으로 여전히 목록에 있습니다. 실제로는 헤더 이름이 여전히 사실상의 관례로 널리 유용하지만, 표준이 완료된 것처럼 행동하기보다는 자체 API에서 계약을 문서화해야 합니다.
키가 무엇을 나타내야 할까요? 의도입니다. HTTP 시도도, TCP 연결도, 재시도 카운터도 아닙니다. 사용자가 “주문 123을 한 번 생성한다"는 의도라면, 그 동일한 명령에 대한 모든 재시도는 동일한 키를 재사용해야 합니다. 사용자가 “두 번째 주문을 한다"는 의도라면, 다른 키를 사용해야 합니다.
요청 ID(Request ID)는 추적(Tracing)을 위한 것입니다. 멱등성 키는 정확성(Correctness)을 위한 것입니다. 이를 혼동하면 대시보드는 깔끔해 보이지만 돈은 두 번 이동하게 됩니다.
PUT만으로는 충분하지 않은 이유
아니요, HTTP PUT만으로는 작업이 멱등해지지 않습니다.
네, RFC 9110은 PUT에 멱등 시맨틱을 부여합니다. 그러나 PUT 핸들러가 하위 이벤트(new downstream event)를 방출하거나, 매번 재시도 시 이메일을 보내거나, 외부 제공자에 대해 다시 청구한다면, 라우트 이름이 존경받을 만해 보일지라도 구현은 비즈니스 계약을 위반한 것입니다.
동사 선택은 클라이언트가 의도를 이해하는 데 도움이 됩니다. 그러나 의도를 구현해 주지는 않습니다.
리소스 모델이 진정한 전체 교체(Full replacement) 또는 업서트(Upsert) 스타일 작업에 부합할 때 PUT를 사용하세요. 명령이나 작업을 생성할 때 POST를 사용하세요. 그러나 네트워크 경계를 가로질러 재시도될 수 있는 모든 변형에 대해서는 명시적인 멱등성 계약을 문서화하세요. 변형 작업이 채팅 워크플로우에서 트리거되는 경우, 동일한 계약이 Slack 통합 패턴: 알림 및 워크플로우와 Discord 통합 패턴: 알림 및 제어 루프에도 적용됩니다. 숨겨진 부수 효과가 아키텍처가 죽는 곳입니다.
멱등성 키는 얼마나 오래 저장해야 합니까
전송 팀이 원하는 것보다 더 오래.
Stripe는 키가 최소 24시간 후에 정리될 수 있다고 말합니다. PayPal은 보유 기간이 API에 따라 다르며 최대 45일까지 지속될 수 있는 예를 제공합니다. Amazon SQS FIFO는 5분 창 내에서만 중복을 제거합니다. GitHub는 수동 재전송을 위해 최근 전달 기록을 3일 동안 유지합니다. 이러한 숫자들은 크게 다르며, 올바른 보유 기간은 프로토콜 기본값이 아닌 비즈니스 결정입니다.
큐가 5분만 유지하기 때문에 키를 5분만 유지한다면, 여러분은 멱등성을 설계하는 것이 아닙니다. 전송의 제한을 비즈니스 레이어로 복사하는 것입니다.
최소한 다음 창들의 최대치를 기준으로 멱등성 기록을 유지하세요:
- 클라이언트 재시도 지평선(Client retry horizon)
- 큐 재드라이브 지평선(Queue redrive horizon)
- 웹훅 재생 지평선(Webhook replay horizon)
- 운영자 재생 지평선(Operator replay horizon)
- 자금 이동 작업의 정산 또는 보상 지평선(Settlement or compensation horizon)
결제, 예약, 프로비저닝의 경우 이는 종종 분식이 아닌 시간 또는 일 단위를 의미합니다.
AWS는 저도 완전히 동의하는 두 가지 반패턴(Anti-pattern)을 지적합니다. 시계 왜곡과 충돌로 인해 신뢰할 수 없으므로 타임스탬프를 키로 사용하지 마세요. 성능과 확장성에 해가 되므로 모든 요청에 대해 전체 요청 페이로드를 중복 제거 기록으로 맹목적으로 저장하지 마세요. 안전하게 재생하는 데 필요한 최소 응답 상태와 정규화된 요청 해시를 저장하세요. 첫 번째 응답 바이트를 그대로 재현해야 하는 경우, Stripe가 사용하는 방식대로 표준 응답 바디를 저장하세요.
멱등성을 현실로 만드는 데이터베이스 패턴
지속성 계층이 경쟁에서 정확히 한 번 승리할 수 있을 때 멱등성은 현실이 됩니다.
PostgreSQL은 여기서 두 가지 중요한 원시(Primitive)를 제공합니다. 고유 제약(Unique constraints)은 한 개 이상의 열에서 고유성을 강제하며, INSERT ... ON CONFLICT는 고유성 위반 시 실패하는 대신 대체 작업을 정의할 수 있게 해줍니다. PostgreSQL은 또한 ON CONFLICT DO UPDATE가 동시성 하에서 원자적인 삽입 또는 업데이트 결과를 보장한다고 문서화하고 있습니다.
이것은 여러분의 멱등성 계층이 보통 다음과 같은 테이블로 시작해야 함을 의미합니다:
create table api_idempotency (
tenant_id text not null,
operation text not null,
idempotency_key text not null,
request_hash text not null,
state text not null,
status_code integer,
response_body jsonb,
resource_type text,
resource_id text,
created_at timestamptz not null default now(),
expires_at timestamptz not null,
primary key (tenant_id, operation, idempotency_key)
);
그리고 처리 흐름은 다음과 같아야 합니다:
begin transaction
try insert (tenant_id, operation, idempotency_key, request_hash, state='pending')
on conflict do nothing
load row for (tenant_id, operation, idempotency_key) for update
if row.request_hash != incoming_request_hash
fail with conflict or validation error
if row.state = 'completed'
return stored response
if row.state = 'pending' and row was created by another live request
either wait briefly, or fail fast with a retryable response
perform local business mutation
store stable result in idempotency row
set state = 'completed'
commit
return result
중요한 부분은 구문이 아닙니다. 중요한 것은 원자성(Atomicity)입니다. 키를 기록하고 변형을 수행하는 것은 함께 성공하거나 실패해야 합니다. AWS는 API 멱등성을 위해 이를 명시적으로 언급하며, 동일한 규칙이 SQL 기반 서비스에도 적용됩니다.
“키를 선택하고; 누락되면 주문을 삽입"과 같은 단순한 확인-다음-실행(Check-then-act) 시퀀스를 수행하지 마세요. 동시성 하에서 두 요청이 확인을 통과하고 둘 다 부수 효과를 생성할 수 있습니다. 고유 제약은 선택 사항이 아닙니다. 이는 여러분의 아키텍처를 낙관적인 민담에서 부하 하에서 증명할 수 있는 무엇인가로 바꾸는 메커니즘입니다.
리뷰에서 제가 사용하는 규칙은 다음과 같습니다. 중복 제거 결정이 변형과 동일한 트랜잭션 경계에 의해 보호되지 않는다면, 여러분에게 멱등성이 있는 것이 아닙니다. 희망만 있을 뿐입니다.
메시지, 이벤트, 웹훅은 자체적인 경계가 필요합니다
소비자는 중복 이벤트와 메시지를 어떻게 처리합니까
메시지 소비자의 경우, 고전적인 패턴이 여전히 올바른 것입니다. 처리된 메시지 ID를 비즈니스 업데이트와 동일한 데이터베이스 트랜잭션에 기록하세요. Chris Richardson은 PROCESSED_MESSAGES 테이블 접근 방식을 직접 설명하며, 구독자와 메시지 ID에 대한 기본 키를 사용하여 중복이 깔끔하게 실패하고 무시될 수 있게 합니다.
많은 팀이 명시적인 processed_messages 스토어를 인박스(Inbox) 테이블이라고 부릅니다. 라벨보다는 규칙이 중요합니다. 수신자는 재시도가 안전하게 아무것도 하지 않을 수 있도록 이미 메시지를 처리했다는 증거를 지속해야 합니다.
최소한의 형태는 다음과 같습니다:
create table processed_messages (
subscriber_id text not null,
message_id text not null,
processed_at timestamptz not null default now(),
primary key (subscriber_id, message_id)
);
그리고 소비자 흐름은 HTTP 흐름만큼 엄격합니다:
begin transaction
insert into processed_messages (subscriber_id, message_id)
values (?, ?)
on conflict do nothing
if no row inserted
rollback
ack and ignore duplicate
apply business mutation
commit
ack message
이 패턴은 지루합니다. 좋습니다. 멱등성은 지루해야 합니다.
또한 브로커 마케팅 용어에 의존하려는 시도보다 일반적으로 더 낫습니다. Kafka의 정확 한 번 지원은 Kafka 자체 트랜잭션 모델 내부에 머무를 때 훌륭하지만, Kafka 문서도 여전히 외부 목적지는 협력이 필요하다고 경고합니다. SQS FIFO는 5분 중복 제거 창 내에서만 중복 전송을 줄입니다. Pub/Sub의 정확 한 번은 여전히 구독자가 확인이 실패할 때 진행 상황을 추적하고 중복 작업을 피하기를 기대합니다.
정확 한 번(Exactly-once)은 보통 로컬 최적화입니다. 멱등 부수 효과가 시스템 보장입니다.
아웃박스 패턴과 중복 제거를 쌍으로 사용
서비스가 로컬 상태를 업데이트하고 이벤트를 게시하는 경우, 멱등 소비만으로는 충분하지 않습니다. 로컬 트랜잭션 커밋 후 이벤트를 안전하게 외부로 보내는 방법도 필요합니다.
그것이 바로 트랜잭셔널 아웃박스 패턴이 중요한 이유입니다. Chris Richardson은 기본 아이디어를 비즈니스 업데이트와 동일한 트랜잭션에서 아웃박스 테이블에 이벤트를 쓰고, 이를 비동기적으로 게시하는 것으로 설명합니다. Debezium은 아웃박스 패턴이 서비스의 내부 상태와 다른 서비스가 소비하는 이벤트 사이의 불일치를 피한다고 말합니다. NServiceBus는 더 나아가 아웃박스 처리가 들어오는 메시지를 중복 제거하고 좀비 기록 및 유령 메시지를 피하는 방법을 보여줍니다.
데이터를 소유하고 통합 이벤트를 게시하는 서비스에 대해 제가 권장하는 아키텍처는 다음과 같습니다:
- 멱등성 키 하에서 명령을 유효성 검사하고 지속합니다.
- 하나의 로컬 트랜잭션에서 비즈니스 상태와 아웃박스 이벤트를 씁니다.
- CDC 또는 아웃박스 디스패처가 이벤트를 게시하도록 합니다.
- 하위 소비기도 멱등하게 만듭니다.
아웃박스는 멱등 소비자가 필요 없다는 것을 제거하지 않습니다. 데이터베이스 커밋과 브로커 게시가 하나의 마법 같은 분산 트랜잭션일 수 있다고 가정할 필요가 없게 만듭니다. 보통은 불가능하기 때문입니다.
웹훅은 브랜드만 더 나은 메시지입니다
входящие(webhook)를 신뢰할 수 없는 네트워크 가장자리에서 온 메시지와 정확히 동일하게 취급하세요.
GitHub는 전달이 순서 없이 도착할 수 있으며, X-Hub-Signature-256을 사용하여 진위성을 검증하고, 고유한 전달 식별자로 X-GitHub-Delivery를 제공한다고 문서화합니다. 또한 재전송은 동일한 전달 ID를 재사용한다고 명시합니다.
따라서 아키텍처는 직관적입니다:
- 먼저 서명을 검증합니다
- 중복 제거 키로 전달 GUID를 사용합니다
- 부수 효과 전에 수신을 지속합니다
- 도착 순서를 가정하기보다 순서에 인지된 핸들러를 만듭니다
- 무거운 작업을 큐에 넣고 빠르게 반환합니다
웹훅 핸들러가 수신을 기록하기 전에 비즈니스 테이블에 직접 쓰는 경우, 이는 프로덕션 준비가 되지 않은 것입니다. 단지 중복 실수를 더 빠르게 만드는 것일 뿐입니다.
사가(Sagas) 및 워크플로우 엔진도 여전히 멱등성이 필요합니다
사가(Sagas) 및 내구형 워크플로우 엔진은 문제를 삭제하지 않습니다. 이를 가시화할 뿐입니다.
Temporal은 활동(Activities)이 실패 또는 타임아웃 후 재시도될 수 있기 때문에 활동을 멱등적으로 작성할 것을 권장합니다. 그 문서들은 심지어 워커가 외부 부수 효과를 성공적으로 완료했지만 완료 보고 전에 충돌하여 활동이 다시 실행되는 경계 사례를 지적합니다. Temporal은 또한 하위 서비스에 호출할 때 워크플로우 실행 ID와 활동 ID의 조합을 안정적인 멱등성 키로 사용하는 것을 제안합니다. 서비스 오케스트레이션에 이를 적용하고 있다면, AI/ML 오케스트레이션을 위한 Go 마이크로서비스에서 더 넓은 워크플로우 트레이드오프를 다룹니다.
그것이 바로 올바른 정신 모델입니다. 워크플로우 엔진은 실행 기록을 보존하고 재시도를 조정할 수 있습니다. 그러나 애플리케이션이 멱등 단계와 멱등 보상을 제공하지 않는 한, 카드의 충전을 되돌리거나 이메일을 보내지 않도록 할 수는 없습니다.
이것은 사가에도 동일하게 적용됩니다. Temporal 자체 사가 가이드는 단계가 실패할 때 실행되는 보상 작업을 설명합니다. 이러한 보상도 멱등해야 합니다. “결제 환불"이 두 번 실행되면, 새로운 버그를 생성함으로써 원래 버그를 해결했을 수 있습니다.
여기서 제 규칙은 잔혹하고 간단합니다. 외부 세계와 상호작용하는 모든 활동(Activity), 모든 명령 핸들러 및 모든 보상은 자연스럽게 멱등하거나 하위 시스템에 실제 멱등성 키를 전달해야 합니다.
프로덕션 전에 멱등성을 테스트하는 방법
대부분의 팀은 해피 패스(Happy path)를 테스트한 후 재시도가 발생하면 놀라고 행동합니다. 이는 충분하지 않습니다. Go 팀의 경우, testing/synctest를 사용한 동시 Go 코드 테스트에서는 인위적인 지연을 통해 잠들지 않고 재시도 루프 및 컨텍스트 마감선 동작에 대한 빠르고 결정론적인 테스트를 작성하는 방법을 다룹니다.
최소한 다음 사례에 대한 자동화된 테스트가 있어야 합니다:
- 서버가 변형을 커밋하지만 응답이 클라이언트에 도달하지 않는 경우
- 동일한 멱등성 키로 두 개의 동일한 요청이 경쟁하는 경우
- 동일한 키가 다른 페이로드와 함께 재사용되는 경우
- 소비자가 데이터베이스 작업을 커밋하고 ack 전에 충돌하는 경우
- 웹훅이 동일한 전달 ID로 재생되는 경우
- 아웃박스 디스패처가 동일한 이벤트를 여러 번 게시하는 경우
- 워크플로우 활동이 외부 호출을 완료하고 완료 보고 전에 충돌하는 경우
- 멱등성 기록이 만료되고 진정한 늦은 재시도가 도착하는 경우
AWS는 성공적인 요청, 실패한 요청, 중복 요청을 포함하는 포괄적인 테스트 스위트 사용을 명시적으로 권장합니다. 이 조언은 평범하지만 절대적으로 정확합니다.
저는 하나 더 실패 훈련을 추가하겠습니다. 재생된 응답이 첫 번째 결과와 시맨틱하게 동등한지 확인하세요. AWS는 늦게 도착하는 재시도를 논의하고, 기본 상태가 변경된 후에도 원래 의미를 보존하는 응답을 주장합니다. 이는 “추가적인 부수 효과가 발생하지 않음"과 “호출자가 여전히 일관된 계약을 가짐” 사이의 차이입니다.
실제 시스템을 구하는 의견이 담긴 규칙들
아키텍처 리뷰에서 강제할 규칙들은 다음과 같습니다.
첫째, 멱등성 키는 전송 시도가 아닌 비즈니스 의도에 속합니다.
둘째, 모든 키를 테넌트와 작업으로 스코핑합니다. 전역 키 공간은 관련 없는 요청이 충돌하는 방식입니다.
셋째, 중복 제거 결정을 변형과 함께 원자적으로 지속합니다. 이것이 사실이 아니라면, 설계는 잘못되었습니다.
넷째, 동일한 키와 다른 페이로드의 재시도를 거부합니다. Stripe와 AWS는 좋은 이유로 이를 수행합니다.
다섯째, 가장 짧은 큐 창이 아닌 비즈니스 프로세스의 전체 재생 지평선을 위해 키를 유지합니다.
여섯째, 프로듀서는 아웃박스와, 소비자는 메시지 ID 추적이 쌍이 되게 합니다. 다른 하나가 없는 한쪽은 반쪽짜리 설계입니다.
일곱째, 비즈니스 작업이 동일할 때 동일한 작업 식별성을 하위로 전파합니다. AWS는 처리 체인을 따라 멱등성 토큰을 전달하는 것을 명시적으로 권장합니다.
여덟째, 정확 한 번 마케팅이 멱등 부수 효과의 필요성을 제거한다고 결코 가정하지 마세요.
그것이 엄격하게 들린다면, 좋습니다. 멱등성은 낙관적인 아키텍처가 프로덕션 현실과 만나는 곳입니다. 모든 곳에 복잡성은 필요하지 않습니다. 그러나 중복된 부수 효과가 돈, 상태 또는 신뢰를 해칠 수 있는 곳에서는 멱등성이 계약의 일류 부분이어야 합니다.
이 동일한 규칙은 백그라운드 AI 에이전트에 직접 적용됩니다. 작업을 주장하고, 알림을 방출하거나, 도구 호출을 트리거하는 폴링 에이전트는 결제 API만큼이나 중복 제거 키와 멱등 주장 프로토콜이 필요합니다. 프로덕션 AI 어시스턴트 내부에서 주장 및 중복 제거 패턴이 어떻게 작동하는지에 대해서는 AI 어시스턴트에서의 폴링 에이전트: 11가지 구현 패턴을 참조하세요.