같은 요청을 여러 번 보내도 서버 상태가 한 번 보낸 것과 같은 성질(idempotency).
수학에서의 정의
- f(f(x)) = f(x)
| 절대값: | abs(abs(-5)) = abs(-5) = 5 | → 멱등 |
|---|---|---|
| 증가: | inc(inc(5)) = 7 ≠ inc(5) = 6 | → 비멱등 |
HTTP 메서드별
| 메서드 | 안전 | 멱등 | 설명 |
|---|---|---|---|
| GET | ○ | ○ | 조회만 |
| HEAD | ○ | ○ | 헤더만 |
| PUT | ✗ | ○ | 전체 교체 — 몇 번 해도 같은 상태 |
| DELETE | ✗ | ○ | 이미 지워졌으면 그대로 |
| POST | ✗ | ✗ | 부를 때마다 새로 생성 |
| PATCH | ✗ | 경우에 따라 | 아래 참조 |
PUT과 PATCH의 차이
- PUT /users/1 { "name": "김철수", "age": 30 }
- 리소스 전체를 이 내용으로 교체. 100번 해도 결과는 같다 → 멱등
| PATCH /users/1 | { "age": 31 } | → 값 설정. 멱등 |
|---|---|---|
| PATCH /users/1 | { "age": "+1" } | → 증가 연산. 비멱등! |
PATCH는 본문의 내용에 따라 갈린다. 그래서 명세에서 "멱등하지 않다"고 규정한다. 설계할 때 절대값 설정 방식으로 만들면 멱등해진다.
DELETE가 멱등인 이유 — 헷갈리는 지점
- DELETE /orders/1 — → 200 삭제됨
- DELETE /orders/1 — → 404 없음
응답 코드가 다른데 왜 멱등인가?
멱등성은 "서버의 상태"에 대한 것이지 "응답"에 대한 것이 아니다. 두 번째 호출 후에도 "주문 1이 없다"는 상태는 동일하다. 그래서 멱등이다.
왜 중요한가 — 재시도
클라이언트: POST /payments 요청
-
네트워크: — 타임아웃 (응답이 안 옴)
-
서버가 처리했을까? 알 수 없다.
-
재시도하면 이중 결제
-
재시도 안 하면 결제가 누락됐을 수 있다
멱등하면 마음 놓고 재시도할 수 있다. 분산 시스템에서 재시도는 불가피하므로, 멱등성은 선택이 아니라 필수 설계 요소다.
비멱등 연산을 멱등하게 만들기
① 절대값 설정으로 바꾼다
- 잔액 = 잔액 + 100 — ← 비멱등
- 잔액 = 1100 — ← 멱등
② 조건부 갱신 (상태 전이)
UPDATE orders SET status = 'PAID'
WHERE id = ? AND status = 'PENDING'; -- 두 번째는 0행 → 무해
상태 머신으로 설계하면 자연스럽게 멱등해진다.
③ 멱등성 키
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
→ 서버가 키를 기억해, 같은 키의 두 번째 요청은 저장된 결과를 반환
POST처럼 본질적으로 비멱등인 것을 멱등하게 만드는 표준 방법이다.
④ 유니크 제약
ALTER TABLE payment ADD UNIQUE (order_id);
-- 두 번째 INSERT 는 DB가 거부한다. 가장 확실한 방어선
메시지 소비에서도 같다
- 브로커는 at-least-once → 중복 전달이 전제
- 소비자가 멱등해야 한다
- 처리 이력 테이블 또는 조건부 UPDATE
"멱등성"은 HTTP만의 개념이 아니라 분산 시스템 전반의 설계 원칙이다.
면접 답변 골격
"네트워크에서는 성공했는데 응답만 유실된 경우와 실패한 경우를 구분할 수 없어 재시도가 불가피합니다. 그래서 상태 변경 API는 멱등하게 설계합니다. 상태 전이를 조건부 UPDATE로 만들거나 멱등성 키를 받고, 최후 방어선으로 유니크 제약을 겁니다."