멱등성이 필요한 이유

한 줄 정의

멱등성은 같은 요청이 한 번 처리되든 여러 번 처리되든 최종 업무 결과가 같도록 만드는 설계 원칙이다.

실무에서 왜 문제 되는가

  • 사용자가 버튼을 여러 번 누르면 같은 API가 중복 호출될 수 있다.
  • 네트워크 timeout 이후 클라이언트나 서버가 같은 요청을 재시도할 수 있다.
  • 메시지 브로커는 장애와 재처리 과정에서 같은 메시지를 다시 전달할 수 있다.
  • 중복 요청이 결제, 쿠폰, 포인트, 재고 차감에 반영되면 금전적 오류가 생긴다.
  • 단순히 “요청을 한 번만 보낸다”는 가정은 운영 환경에서 깨진다.

동작 원리

  1. 요청을 구분할 수 있는 기준을 정한다.
  2. 이미 처리된 요청인지 확인한다.
  3. 처음 보는 요청이면 업무 처리를 수행하고 처리 결과를 저장한다.
  4. 이미 처리된 요청이면 기존 결과를 반환하거나 no-op으로 끝낸다.
  5. 실패와 timeout은 성공, 실패, 결과 불명 상태를 구분해 복구한다.

실무 판단 기준

상황선택이유
조회 API별도 멱등 설계가 거의 필요 없음같은 조회는 상태를 바꾸지 않는다
생성/결제/차감 API멱등성 필수중복 처리되면 업무 결과가 달라진다
외부 API 재시도멱등키 사용timeout 후 중복 부작용을 막아야 한다
메시지 소비처리 이력 저장at-least-once 전달에서는 중복 소비가 가능하다
단순 상태 변경조건부 update이미 처리된 상태를 다시 처리하지 않는다

자주 나는 실수

  • 멱등성을 HTTP 메서드 성질로만 이해한다.
  • timeout을 실패로 단정하고 같은 결제 요청을 새로 보낸다.
  • 프론트엔드 버튼 비활성화만으로 중복 요청을 막으려 한다.
  • lock을 사용하면 멱등성이 필요 없다고 생각한다.
  • 처리 결과를 저장하지 않아 중복 요청에 같은 응답을 줄 수 없다.

확인 방법

  • 테스트: 같은 요청을 여러 번 보내도 row 수, 금액, 재고가 변하지 않는지 확인한다.
  • 로그: idempotency key, request id, 처리 상태, 기존 응답 반환 여부를 남긴다.
  • 메트릭: duplicate request count, idempotency hit count, retry count를 본다.
  • 운영 확인: pending 또는 result-unknown 상태가 오래 남지 않는지 본다.

장점과 한계

장점한계
재시도와 중복 클릭에 안전해진다요청 식별 기준과 저장소가 필요하다
장애 복구를 단순하게 만든다보관 기간과 저장 비용을 정해야 한다
외부 시스템 연동 안정성이 높아진다모든 부작용을 자동으로 되돌리지는 못한다

짧은 예제

public PaymentResponse approve(PaymentRequest request) {
    return idempotencyService.execute(
        request.idempotencyKey(),
        () -> paymentService.approve(request)
    );
}

같은 idempotencyKey로 들어온 요청은 새로 결제 승인 로직을 실행하지 않고, 저장된 처리 결과를 기준으로 응답한다.

핵심 요약

멱등성은 같은 요청이 반복되어도 최종 업무 결과가 깨지지 않게 만드는 설계다.

실무에서는 중복 클릭, 네트워크 재시도, timeout, 메시지 재처리 때문에 필요하다.

멱등성은 lock이나 트랜잭션과 다르며, 이미 처리된 요청을 식별하고 같은 결과로 수렴시키는 것이 핵심이다.

결제, 쿠폰, 포인트, 재고처럼 상태를 바꾸는 기능에서는 반드시 검토해야 한다.

중복 요청을 막는 것보다 중복 요청이 와도 안전하게 처리하는 쪽이 운영 환경에 더 적합하다.

꼬리 질문

  • 멱등성과 중복 요청 방지는 무엇이 다른가?
  • timeout 이후 재시도할 때 어떤 위험이 있는가?
  • lock을 사용해도 멱등성이 필요한 이유는 무엇인가?

관련 문서