Dev Log

[Backend] 멱등성(Idempotency): API 설계의 필수 안전장치

odong 2026. 7. 14. 21:42

백엔드 API를 설계할 때 기능의 정상 가동만큼이나 중요한 것이 바로 예외 처리와 안정성입니다. 특히 돈이 오가는 결제나 주문 시스템을 다룰 때 가장 무서운 시나리오는 유저가 결제 버튼을 연달아 누르는 '더블 클릭' 현상입니다.

시스템에 아무런 안전장치가 없다면, 유저의 실수 한 번으로 결제가 두 번 처리되는 대참사가 발생합니다. 이러한 중복 요청 문제를 아키텍처 레이어에서 완벽하게 가드레일 쳐주는 핵심 개념이 바로 멱등성입니다.

1. 멱등성이란

수학에서 멱등성이란 "연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질"을 뜻합니다. 예를 들어 어떤 수에 1을 곱하는 연산($f(x) = x \times 1$)은 100번을 연속해서 수행해도 결과가 처음과 똑같으므로 멱등합니다.

이를 HTTP API 프로토콜에 대입해 보면 다음과 같습니다.

API 멱등성이란: 동일한 요청을 한 번 보내는 것과, 연속해서 백 번 보내는 것의 서버 측 시스템 상태가 동일함을 보장하는 성질.

 HTTP Method별 멱등성 가이드

HTTP 메서드 멱등성 여부 메커니즘 설명
GET O 데이터를 몇 번을 조회하든 서버의 데이터 상태는 변하지 않음.
PUT O 특정 리소스를 완전히 덮어쓰는 요청이므로, 동일한 값으로 여러 번 날려도 최종 상태는 같음.
DELETE O 데이터를 삭제하는 요청. 데이터가 지워지고 나면, 이후 요청은 "데이터 없음(404)"을 뱉을 뿐 서버 상태는 동일하게 '삭제됨' 유지.
POST X [멱등하지 않음] 요청마다 새로운 리소스를 생성하므로, 누른 횟수만큼 DB에 레코드가 쌓임.

결제나 주문은 대개 POST 메서드를 사용하기 때문에 멱등성이 깨져 있으며, 별도의 아키텍처 설계가 강제됩니다.

2. 실무 멱등성 방어벽: Idempotency-Key 아키텍처

실무에서 표준처럼 사용하는 방식이 바로 Idempotency-Key를 활용한 예외 처리입니다.

[클라이언트] -(1. POST API + Idempotency-Key: UUID)-> [백엔드 API]
                                                          |
                                                    (2. Redis 조회)
                                                    - 키가 없어 결제 진행
                                                    - Redis에 결과 저장
                                                          |
[클라이언트] -(3. 네트워크 지연으로 재요청 / 키 동일)-> [백엔드 API]
                                                          |
                                                    (4. Redis 조회)
                                                    - 이미 처리된 키
                                                    - 실제 결제는 패스
                                                    - 저장된 결과만 반환

구체적인 3단계 가동 파이프라인

  1. 클라이언트 고유 키 발급: UUID를 생성하여, API  헤더에 Idempotency-Key: "unique-uuid-string" 형태로 보냅니다.
  2. 서버의 분산 캐시 검증: 백엔드는 요청을 받자마자 Redis 같은 인메모리 저장소에서 해당 키를 먼저 조회합니다.
    • 최초 요청인 경우: Redis에 키를 등록하고 결제 로직을 수행한 뒤, 값을 Redis에 보관(TTL 만료 시간 설정 필수)합니다.
    • 중복 요청인 경우: 똑같은 키로 요청이 들어오면, Redis에 있던 직전 응답을 그대로 복사해서 즉시 반환합니다.

3. 멱등성 검증 인터셉터 예시 스크립트

백엔드 진영에서 공통 레이어로 중복 요청을 가드레일 치는 수도코드 스타일의 아키텍처 구조입니다.

def handle_payment_request(request):
    # 1. 헤더에서 멱등성 키 추출
    idempotency_key = request.headers.get("Idempotency-Key")
    
    if not idempotency_key:
        return response(status=400, message="Idempotency-Key가 누락되었습니다.")
        
    # 2. Redis 분산 락 및 기존 키 스캔
    cached_response = redis.get(idempotency_key)
    
    if cached_response:
        # 똑같은 키가 이미 존재하면 로직을 태우지 않고 캐싱된 응답 반환 (멱등성 보장)
        return json.loads(cached_response)
        
    # 3. 최초 요청이라면 실제 핵심 비즈니스 로직 가동
    payment_result = execute_actual_payment(request)
    
    # 4. 다음 중복 요청 방어를 위해 Redis에 결과 세팅 (유효기간 24시간)
    redis.setex(idempotency_key, timedelta(days=1), json.dumps(payment_result))
    
    return payment_result

네트워크 지연까지 고려한 완벽한 방어

네트워크 환경은 언제나 불안정합니다. 유저가 버튼을 한 번만 눌렀더라도, 서버는 결제를 성공했는데 중간 기지국 문제로 유저 브라우저까지 응답 패킷이 도달하지 못할 수 있습니다. 이때 브라우저는 멱등성 키를 가지고 재시도 메커니즘을 가동합니다.

Idempotency-Key 가드레일을 촘촘히 쳐두면, 분산 환경 속에서 네트워크 재요청이 몰아치더라도 시스템 정합성을 방어할 수 있습니다. 견고한 결제 인프라 설계를 고민 중이라면 적용해 보시기 바랍니다.