멱등성과 재시도 (Idempotency and Retry)

목차


왜 이 주제를 묻는가

멱등성과 재시도는 단순한 API 테크닉이 아니라, 분산 시스템에서 중복 요청과 부분 실패를 어떻게 흡수할지를 묻는 질문입니다.

이 문서는 재시도가 안전하려면 왜 멱등성과 중복 흡수가 필요한지에 집중합니다. timeout, retry 위치, backoff, circuit breaker 같은 복원력 일반론은 복원력 패턴 (Timeout, Retry, Circuit Breaker) 문서가 더 직접적인 기준 문서입니다.

면접관은 보통 다음을 확인하려고 이 질문을 던집니다.

  • 네트워크 타임아웃이 나도 같은 작업을 다시 시도해도 안전한가
  • 같은 요청이 두 번 도착해도 데이터가 깨지지 않는가
  • 어떤 경계에서 idempotency key나 dedupe 장치가 필요한가
  • 중복 요청, 중복 메시지, 보상 작업을 구분해서 설명할 수 있는가

즉, 이 주제는 “에러가 나면 다시 보내면 된다”가 아니라 다시 보내도 같은 결과로 수렴하게 만드는 문제입니다.


멱등성과 재시도는 다르다

두 개념은 자주 같이 나오지만 역할이 다릅니다.

항목 멱등성(Idempotency) 재시도(Retry)
성격 요청 또는 작업의 성질 실패한 요청을 다시 보내는 정책
질문 같은 요청이 여러 번 와도 결과가 같은가 실패했을 때 다시 시도해야 하는가
책임 서버와 데이터 모델 클라이언트, 워커, 게이트웨이, 소비자
위험 중복 처리, 중복 부작용 retry storm, 지연 증가, 부하 증폭

멱등성은 “같은 입력을 여러 번 적용해도 최종 상태가 같은가”를 보는 성질입니다.
재시도는 “실패한 호출을 다시 보내서 성공 확률을 높일 것인가”를 정하는 운영 정책입니다.

이 둘을 분리해서 생각해야 합니다. 재시도는 멱등성을 대신하지 못하고, 멱등성은 재시도를 자동으로 보장하지 않습니다.


왜 재시도와 같이 묶여 나오는가

재시도는 결국 같은 작업을 다시 보내는 행위이기 때문에,
멱등성이 없으면 성공 확률을 높이는 대신 중복 부작용을 만들 수 있습니다.

예를 들어:

  • 결제 승인 요청이 timeout 뒤 다시 들어옴
  • 메시지 소비자가 처리 후 ack 전에 죽어 같은 이벤트를 다시 받음
  • 배치 작업이 중간 실패 후 같은 단계부터 다시 시작됨

이때 필요한 질문은 retry 정책 자체보다 먼저 다음입니다.

  • 같은 작업임을 어떻게 식별할 것인가
  • 이미 처리한 작업을 어떻게 알아볼 것인가
  • 중복 요청이 와도 어떤 응답을 돌려줄 것인가

즉, 재시도와 멱등성이 같이 나오는 이유는
재시도가 중복 가능성을 만들고, 멱등성이 그 중복을 흡수하기 때문입니다.


어떤 작업이 멱등해야 하는가

멱등성은 모든 곳에 똑같이 필요한 것이 아니라, 실패 후 같은 요청이 다시 들어올 가능성이 높은 경계에 먼저 필요합니다.

대상 왜 멱등해야 하는가 실무 장치
HTTP API 클라이언트 재시도, 로드 밸런서 재전송, 네트워크 timeout으로 같은 요청이 다시 도착할 수 있음 Idempotency-Key, unique constraint, upsert
메시지 소비자 at-least-once 전달에서는 같은 이벤트를 다시 받을 수 있음 processed-message table, dedupe key, 상태 전이 검사
배치 / 워크플로 작업 재시작, 실패 후 재실행, 운영자 수동 재처리로 같은 단계가 다시 돌 수 있음 체크포인트, 단계별 상태 저장, 상태 전이 검사

이 중에서도 면접에서 가장 자주 나오는 경계는 다음입니다.

  • 결제, 주문 생성, 환불 같은 POST API
  • Kafka / RabbitMQ 소비자
  • 야간 배치와 재처리 작업

관련 구현 상세는 메시징 시스템, 메시징 및 이벤트 기반 아키텍처 (Event-Driven Architecture) 문서를 같이 보면 좋습니다.


HTTP 메서드와 idempotency key

HTTP에는 메서드 자체의 멱등성이 있습니다. RFC 9110은 PUT, DELETE, 그리고 safe method를 멱등하다고 정의합니다.1

하지만 면접에서 중요한 점은 HTTP 메서드의 멱등성비즈니스 작업의 멱등성이 같지 않다는 것입니다.

  • PUT /orders/123 처럼 리소스를 덮어쓰는 요청은 메서드 의미상 멱등하게 설계할 수 있습니다.
  • POST /orders 처럼 새 리소스를 생성하는 요청은 기본적으로 멱등하지 않습니다.
  • 다만 POST 이더라도 Idempotency-Key를 붙여 서버가 같은 작업으로 인식하게 만들면 실무에서는 재시도 가능한 API로 만들 수 있습니다.23
구분 의미 예시
HTTP 메서드 멱등성 프로토콜 차원의 의미 PUT, DELETE, safe method
비즈니스 멱등성 같은 작업이 두 번 와도 한 번만 반영되는 성질 결제 승인, 주문 생성, 환불
idempotency key 같은 작업임을 서버가 식별하는 토큰 Idempotency-Key: abc-123

실무에서는 보통 다음처럼 설명하면 좋습니다.

  • HTTP 메서드가 멱등하다고 해서 항상 안전한 것은 아니다.
  • POST라도 idempotency key를 두면 재시도 가능한 API로 만들 수 있다.
  • 같은 key에 다른 payload가 오면 같은 작업으로 취급하면 안 된다.

Stripe는 같은 key를 일정 기간 재사용하는 시나리오를 안내하고, 같은 키에 다른 파라미터가 들어오면 오류로 처리하는 방향을 설명합니다.2


idempotency key 저장 전략

idempotency key는 보통 “키만 저장”하는 것이 아니라, 같은 요청인지 검증하고 같은 결과를 돌려줄 수 있도록 필요한 메타데이터까지 함께 저장합니다.

대표적으로 다음 항목이 들어갑니다.

  • idempotency key
  • 요청 payload의 해시
  • 처리 상태
  • 최종 응답 또는 응답 참조
  • 만료 시각 (expires_at)

같은 key가 동시에 들어오는 상황도 따로 생각해야 합니다. 실무에서는 보통 상태를 in_progress -> completed / failed처럼 관리하고, 첫 요청만 key를 선점하도록 만듭니다. 뒤에 들어온 요청은 진행 중 상태를 보고 대기, 재시도, 또는 충돌 응답 중 하나로 처리합니다.

여기서 중요한 것은 idempotency record 생성과 실제 비즈니스 쓰기의 원자성 경계입니다. 가능한 한 같은 트랜잭션이나 원자적 갱신 경계 안에 두는 편이 좋습니다. 그렇지 않으면 key는 저장됐는데 실제 작업은 실패했거나, 반대로 작업은 성공했는데 key 저장이 실패한 틈이 생길 수 있습니다.

전략 적합한 경우 장점 한계
같은 업무 테이블에 unique constraint 단일 행 생성이나 자연키가 분명한 경우 구현이 단순하고 일관성이 좋음 여러 단계 작업에는 맞추기 어렵다
별도 idempotency 테이블 POST 기반 생성 API, 외부 API 연동 요청 이력과 응답 재사용이 쉽다 저장소와 정리 로직이 필요하다
캐시/Redis + TTL 짧은 재시도 창을 빠르게 흡수하고 싶을 때 조회가 빠르고 운영이 가볍다 캐시 만료 뒤 중복이 새어 나갈 수 있다

TTL은 너무 짧으면 중복 재전송을 흡수하지 못하고, 너무 길면 저장 비용과 관리 비용이 늘어납니다.
실무에서는 클라이언트와 네트워크가 실제로 다시 보낼 수 있는 시간 창을 기준으로 TTL을 잡는 편이 좋습니다.

핵심은 “얼마 동안 같은 작업으로 볼 것인가” 입니다.
이 기준이 없으면 같은 key가 재사용되었을 때 오래된 결과를 돌려줄지, 충돌로 볼지, 새 요청으로 볼지 판단할 수 없습니다.


중복 처리와 상태 전이

재시도는 결국 중복을 만들 수 있습니다. 그래서 멱등성과 duplicate handling은 같이 가야 합니다.

대표적인 흡수 방식은 다음과 같습니다.

  • unique constraint: 같은 비즈니스 키가 두 번 들어오면 두 번째를 막음
  • upsert: 존재하면 갱신하고 없으면 생성
  • processed-message table: 이미 처리한 메시지는 무시
  • 상태 전이 검사: 이미 완료된 작업은 다시 진행하지 않음

메시지 소비자에서는 특히 offset commit 시점이 중요합니다.
처리 완료 전에 offset을 커밋하면 메시지를 잃을 수 있고, 처리 후 커밋 전에 장애가 나면 같은 메시지를 다시 받을 수 있습니다. 그래서 소비자 측 멱등성은 사실상 기본 요구사항에 가깝습니다.

또한 상태 전이 자체를 제한하는 것도 중요합니다.

  • 이미 completed 인 작업을 다시 승인하지 않기
  • cancelled -> completed 같은 불가능한 전이를 막기
  • 처리 중(in_progress) 상태에 대한 동시 요청 정책을 정하기

즉, 멱등성은 key 하나 저장하는 기능이 아니라
상태 모델과 결과 재사용 정책을 함께 설계하는 문제입니다.


보상과의 차이

보상 작업은 멱등성을 대체하지 않습니다.

  • 멱등성: 같은 작업이 여러 번 와도 결과가 한 번만 반영되게 함
  • 보상: 이미 일어난 작업을 반대 방향의 새 작업으로 상쇄함

예를 들어:

  • 중복 결제를 막는 것은 멱등성 문제
  • 이미 승인된 결제를 취소하는 것은 보상 문제

면접에서는 이 둘을 섞지 않는 편이 좋습니다.
보상은 실패를 “되돌리는” 접근이고, 멱등성은 애초에 중복 반영이 일어나지 않게 하는 접근입니다.


자주 하는 실수

  • 멱등성을 “재시도 가능”과 같은 뜻으로 써버림
  • 같은 요청을 식별할 key 없이 retry만 넣음
  • key 저장과 실제 비즈니스 쓰기의 원자성 경계를 무시함
  • POST는 무조건 멱등하지 않다고 단정함
  • 메시지 중복 소비를 예외 상황처럼 취급함
  • 상태 전이 검증 없이 중복 요청을 허용함
  • 보상 트랜잭션이 있으니 멱등성은 필요 없다고 생각함

면접 포인트

  • 멱등성은 요청의 성질이고, 재시도는 실패 대응 정책입니다.
  • 재시도가 안전하려면 같은 작업을 식별하고 중복을 흡수하는 장치가 필요합니다.
  • Idempotency-Key, unique constraint, 상태 전이 검사는 대표적인 중복 흡수 장치입니다.
  • 메시지 소비자, 배치, POST API는 멱등성이 특히 중요하게 나오는 경계입니다.
  • retry 위치, timeout, backoff, circuit breaker는 복원력 패턴 (Timeout, Retry, Circuit Breaker) 문서와 함께 설명하면 연결이 좋습니다.

참고 자료