티스토리 뷰
많은 개발자들이 API를 개발할 때 비즈니스 로직, 응답 속도, 동시성 제어, 트랜잭션 처리에는 신경쓰지만 '클라이언트가 동일한 요청을 여러번 보낼 수 있다.'는 사실은 간과하기 쉽다.
실무에서는 다양한 이유로 중복 요청이 발생한다. 이 때 API가 멱등성을 보장하지 못한다면 중복 결제, 중복 재고 차감, 중복 주문 등 다양한 오류가 발생할 수 있다.
이 글에서는 멱등성이 무엇인지 살펴보고, 어떻게 보장할 수 있는지 상세히 설명한다.
멱등성이란?
동일한 연산・요청을 여러번 수행하더라도 최종 결과가 한 번 수행했을 때와 동일한 성질
멱등성(Idempotency)은 동일한 연산・요청을 여러번 수행하더라도 최종 결과가 한 번 수행했을 때와 동일한 성질을 말한다.
즉, 클라이언트가 같은요청을 여러번 보내더라도 서버에서 생성되는 리소스는 변하지 않아야 함을 의미한다.
응답받는 상태 코드(200/409 등)는 달라질 수 있으나 결과(리소스 상태)는 동일해야 한다.
멱등성은 왜 중요한가?
클라이언트는 다양한 이유로 중복 요청을 보낼 수 있다.

- 중복 요청 : 버튼을 연속으로 눌러 중복 요청을 보낼 수 있음
- 서버 지연 : 응답이 늦어지면 클라이언트는 요청이 실패했다고 판단하고 재전송할 수 있음
- 네트워크 불안정 : 네트워크 불안정으로 인해 응답값을 정상적으로 받지 못한 경우 클라이언트는 중복 요청을 보낼 수 있음
이런 상황에서 멱등성이 보장되지 않으면 중복 결제, 중복 주문 등과 같은 심각한 문제가 발생한다.
클라이언트 요청 종류에 따른 멱등성 보장 여부
클라이언트에서 보내는 HTTP 요청 종류에 따라 기본적인 멱등성 보장 여부가 다르다.
| HTTP | Idempotency |
|---|---|
| GET | ✅ |
| HEAD | ✅ |
| OPTIONS | ✅ |
| TRACE | ✅ |
| PUT | ✅ |
| DELETE | ✅ |
| POST | ❌ |
| PATCH | ❌ |
| CONNECT | ❌ |
POST, PATCH 등 기본적으로 멱등성이 보장되지 않는 메서드의 경우 애플리케이션 레벨에서 멱등성을 보장해야한다.
멱등성은 어떻게 보장할 수 있을까?
HTTP 통신은 기본적으로 무상태(Stateless) 이기 때문에 서버는 클라이언트의 요청이 이전과 같은 요청인지 판단할 수 없습니다. 따라서 클라이언트는 서버가 요청을 식별할 수 있는 추가 정보를 제공해야 한다.
1. 멱등키(Idempotency-Key) 활용
클라이언트는 요청 시 헤더에 멱등키(Idempotency-Key)를 포함한다.
Idempotency-Key: 64b7c2a0-...-9fae
서버는 이 키를 기반으로
- 이전에 처리한 요청인지 확인
- 이미 처리됐다면 저장된 응답을 반환
- 처음 온 요청이라면 정상 처리 후 결과를 저장
| idempotency_key | status | created_at |
|---|---|---|
abc123 |
SUCCESS | 2025-11-23 14:31:02 |
xyz789 |
FAIL | 2025-11-23 14:32:10 |
pqr555 |
PROGRESS | 2025-11-23 14:33:18 |
2. 데이터베이스 제약조건 활용
생성하려는 리소스가 자연스럽게 유니크한 키를 갖는 경우 DB 레벨에서 Unique 제약조건을 활용해 멱등성을 보장할 수 있다.
아래는 동일한 이메일로 회원가입 요청이 여러 번 들어온 경우의 예시이다.
| 요청 번호 | 결과 | |
|---|---|---|
| 1 | test@example.com | INSERT 성공 → 회원가입 완료 |
| 2 | test@example.com | UNIQUE 충돌 → 중복 가입 방지 |
| 3 | test@example.com | UNIQUE 충돌 → 기존 사용자 유지 |
한줄 요약
이처럼 멱등성은 서비스의 안정성과 데이터 일관성을 보장하는 핵심 설계 원칙이다.
특히 결제, 재고, 주문처럼 중복 요청이 치명적인 도메인에서는 반드시 고려해야 할 필수 요소다.
API를 설계 단계에서 멱등성은 항상 함께 고민해야 할 요소다.
'라이브러리&프레임워크 > Spring' 카테고리의 다른 글
| 유비쿼터스 언어 (0) | 2026.03.01 |
|---|---|
| Bounded Context란 무엇인가? (0) | 2026.03.01 |
| JPA 엔티티에서 Setter 대신 Builder 패턴을 사용해야 하는 이유 (0) | 2025.11.10 |
| Spring 개발자를 위한 캐싱 전략: 로컬 캐시부터 Redis 분산 캐시까지 (0) | 2025.07.27 |
| Spring Cache 사용법 정리 (0) | 2025.07.27 |
