버튼 한 번 눌렀을 뿐인데 결제가 두 번 됐습니다
개발을 하다 보면 “이 API는 멱등해야 합니다”라는 말을 접하게 됩니다.
REST API, 결제 시스템, 분산 환경을 이야기할 때 특히 자주 등장합니다.
하지만 막상 멱등성이 무엇인지 설명하려고 하면, 개념이 흐릿해지는 경우가 많습니다.
멱등성이 무엇인지, 그리고 왜 실무에서 중요한지를 정리해 보겠습니다.
멱등법칙(冪等法則) 또는 멱등성(冪等性, 영어: idempotent)은 수학이나 전산학에서 연산의 한 성질을 나타내는 것으로, 연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질을 의미한다 -wikipedia
수학적 정의는 이렇습니다.

매우 직관적입니다. 어떤 원소 x에 대해 함수f 를 여러번 연산해도 그 결과는 같습니다.
왜 같은 요청이 여러 번 도착할까요?
우리는 보통 하나의 요청이 서버에 도착하면, 한 번 처리되고 끝난다고 생각합니다.
하지만 실제 운영 환경에서는 그렇지 않습니다.
다음과 같은 상황은 매우 흔합니다.
- 네트워크 지연이나 타임아웃
- 클라이언트의 자동 재시도
- 사용자의 새로고침
- 서버 장애 후 재요청
- 결제사의 웹훅 중복 전송
이 경우 같은 요청이 여러 번 서버에 도착할 수 있습니다.
이것은 예외적인 상황이 아니라, 분산 시스템에서는 정상적인 상황에 가깝습니다.
문제는 이때입니다.
같은 요청이 다시 들어왔을 때, 시스템은 어떻게 동작해야 할까요?
“다시 실행해도 안전하다”는 개념
이 질문에 대한 하나의 해답이 멱등성(Idempotency) 입니다.
x = 5;
이 코드는 몇 번 실행하더라도 결과는 항상 동일합니다. 이와 같은 연산은 멱등적이라고 할 수 있습니다.
반면 다음 코드는 다릅니다.
x++;
실행할 때마다 결과가 달라집니다. 이 연산은 멱등적이지 않습니다.
여기서 중요한 것은 실행 횟수가 아니라, 최종 상태가 변하는지 여부입니다.
멱등적인 작업과 멱등적이지 않은 작업
실제 개발에서 자주 마주치는 예로 비교해 보겠습니다.
멱등적인 작업
- 사용자의 상태를 특정 값으로 설정
- 리소스를 삭제
- 설정 값을 덮어쓰기
user.setStatus(ACTIVE);
이미 상태가 ACTIVE라면, 다시 실행하더라도 결과는 동일합니다.
이러한 작업은 멱등적입니다.
멱등적이지 않은 작업
- 포인트 증가
- 잔액 차감
- 주문 생성
balance -= 1000;
이 코드는 실행할 때마다 상태가 변경됩니다. 같은 요청이 두 번 처리되면, 바로 문제가 발생할 수 있습니다.
HTTP와 멱등성
HTTP 프로토콜 역시 멱등성을 중요한 개념으로 다룹니다.
| method | 멱등성 | 설명 |
|---|---|---|
| GET | O | 리소스를 조회만 함 |
| PUT | O | 리소스를 동일한 상태로 덮어씀 |
| DELETE | O | 이미 삭제된 상태에서도 결과가 동일 |
| POST | X | 요청마다 새로운 리소스를 생성 |
여기서 주목할 점은 다음입니다.
POST 메서드는 기본적으로 멱등하지 않습니다.
따라서 주문 생성, 결제 요청과 같은 API는 별도의 멱등성 설계가 필요합니다.
결제 시스템에서의 멱등성
결제는 멱등성이 특히 중요한 영역입니다.
- 결제 요청 후 타임아웃 발생
- 클라이언트는 실패로 인식하고 재시도
- 서버에서는 이미 결제가 성공한 상태
이 상황에서 멱등성이 보장되지 않으면, 같은 결제가 여러 번 처리될 수 있습니다.
이를 방지하기 위해 Stripe, Toss Payments와 같은 결제 서비스는 Idempotency Key를 사용합니다.
POST /payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
같은 키로 요청이 들어오면,
- 이미 처리된 요청이라면 이전 결과를 반환하고
- 처음 처리되는 요청이라면 결제를 수행한 뒤 결과를 저장합니다
이를 통해 중복 결제를 원천적으로 방지합니다.
멱등성이 없을 때 발생하는 문제
멱등성이 고려되지 않은 시스템에서는 다음과 같은 문제가 발생할 수 있습니다.
- 결제가 중복으로 처리됨
- 주문이 여러 번 생성됨
- 환불이 중복 수행됨
- 장애 복구 이후 데이터 정합성이 깨짐
이 문제들의 공통점은 명확합니다.
실패 자체가 문제가 아니라, 재시도가 위험해진다는 점입니다.
멱등성은 실패를 없애는 기술이 아닙니다.
실패 이후에도 안전하게 다시 시도할 수 있도록 만드는 설계 원칙입니다.
구현은?
일반적인 구현은 상태머신(state machine) 을 이용합니다.
상태 머신의 핵심은 두 가지입니다.
- 시스템이 가질 수 있는 명확한 상태(state)가 존재하고
- 그 상태들 사이의 전이 규칙이 통제된다는 점입니다.
멱등키를 사용하는 결제나 주문 처리 로직은 이 두 조건을 정확히 만족합니다.
멱등키 기반 요청은 하나의 상태 머신 인스턴스입니다
멱등키 하나는 “결제 한 건”을 대표하며, 이 결제는 다음과 같은 상태를 가질 수 있습니다.
INIT : 요청만 생성된 상태
PROCESSING : 외부 시스템(PG) 처리 중
SUCCESS : 결제 성공
FAILED : 결제 실패
그리고 상태 전이는 명확한 규칙을 따릅니다.
INIT -> PROCESSING
PROCESSING -> SUCCESS
PROCESSING -> FAILED
SUCCESS -> (전이 없음)
SUCCESS 상태에 도달한 이후에는 같은 요청이 다시 들어와도 상태가 변하지 않는다는 것입니다.
이것이 바로 멱등성이 보장되는 이유입니다.
마무리
멱등성은 특정 프레임워크나 라이브러리의 기능이 아닙니다. 네트워크는 실패하고, 요청은 중복될 수 있습니다. 이를 예외로 취급하는 순간 시스템은 불안정해집니다.
멱등성을 고민하기 시작했다면, 이미 더 안정적인 시스템을 설계하고 계신 것입니다.
참고



Member discussion