견고한 API 작업을 위한 멱등성 보장
James Reed
Infrastructure Engineer · Leapcell

소개
복잡한 백엔드 개발 세계에서 안정성과 복원력은 무엇보다 중요합니다. API를 설계하고 소비할 때 네트워크 불안정성이나 일시적인 오류로 인해 발생하는 일반적인 문제가 있습니다. 바로 요청을 재시도해야 하는 경우입니다. 읽기 전용 작업(GET)의 경우 재시도는 일반적으로 안전합니다. 그러나 POST(리소스 생성) 또는 PATCH(리소스 업데이트)와 같이 데이터를 수정하는 작업의 경우 단순히 요청을 재시도하면 의도하지 않은 부작용이 발생할 수 있습니다. 사용자가 주문을 제출했는데 네트워크 오류로 인해 요청이 시간 초과되었다고 상상해 보세요. 사용자가 적절한 안전장치 없이 제출을 자동으로 재시도하면 동일한 주문이 두 개 발생하여 데이터 불일치와 고객 불만을 초래할 수 있습니다. 여기서 멱등성 개념이 중요해집니다. API에 멱등성 키를 구현함으로써 동일한 매개변수로 작업을 여러 번 호출하는 것이 한 번 호출하는 것과 동일한 효과를 갖도록 하여 시스템을 더욱 강력하고 사용자 친화적으로 만들 수 있습니다.
멱등성과 멱등성 키 이해
구현 세부 사항을 살펴보기 전에 몇 가지 핵심 용어를 명확히 해 보겠습니다.
- 멱등성: API의 맥락에서 어떤 작업을 여러 번 실행해도 한 번 실행하는 것과 동일한 결과를 생성하면 해당 작업은 멱등성이 있다고 합니다. 이는 응답이 항상 동일하다는 것을 의미하지는 않습니다(예:
POST는 성공적인 첫 번째 시도에 대해201 Created를 반환하고 동일한 멱등성 키로 후속 시도에 대해서는200 OK또는202 Accepted를 반환하여 해당 리소스가 이미 존재하거나 작업이 이미 진행 중임을 나타낼 수 있습니다). 중요한 것은 서버 측의 상태 변경이 동일하다는 것입니다. - 멱등성 키: 요청과 함께 제공되는 고유한 클라이언트 생성 문자열입니다. 서버는 이 키를 사용하여 중복 요청을 감지합니다. 서버가 특정 시간 내에 동일한 멱등성 키로 여러 요청을 받으면 원본 작업의 재시도로 식별하고 다시 처리하지 않도록 할 수 있습니다.
멱등성 키의 원리는 상대적으로 간단합니다. 클라이언트는 특정 작업에 대한 고유 식별자를 생성하고 이를 요청과 함께 보냅니다. 그런 다음 서버는 미리 정의된 기간 동안 이 키(종종 요청 결과와 함께)를 저장합니다. 후속 요청이 동일한 키로 도착하면 서버는 이전에 계산된 결과를 반환하거나 작업이 이미 성공적으로 처리되었음을 확인할 수 있으며 핵심 로직을 다시 실행하지 않습니다.
구현 전략
멱등성 키를 구현하려면 일반적으로 다음 단계가 포함됩니다.
-
클라이언트 측 키 생성: 클라이언트는 시도하는 각 고유 작업에 대해 보편적으로 고유한 식별자(UUID)를 생성해야 합니다. 이 키는 요청을 보내기 전에 생성해야 하며 해당 작업의 재시도에 재사용되어야 합니다.
-
서버 측 키 저장 및 조회:
- 전처리:
Idempotency-Key헤더가 있는 요청을 받으면 서버는 먼저 이 키를 이전에 본 적이 있고 연결된 작업이 완료되었는지 확인합니다. - 실행(첫 번째 시도): 키가 새롭다면 서버는 핵심 비즈니스 로직 실행을 진행합니다. 성공적인 실행 전이나 후에
Idempotency-Key를 요청 매개변수, 응답(상태 코드, 본문) 및 만료 타임스탬프와 함께 저장합니다. - 실행(후속 재시도): 키를 찾은 경우 서버는 연결된 작업의 상태를 확인합니다. 성공적으로 완료되었다면 비즈니스 로직을 다시 실행하지 않고 초기 성공 시도의 저장된 응답을 즉시 반환합니다. 원본 작업이 아직 진행 중이라면 서버는 (정책에 따라)
409 Conflict또는429 Too Many Requests를 반환하거나 완료를 기다릴 수 있습니다.
- 전처리:
-
멱등성 키 데이터 저장: 이 데이터는 영구적이고 효율적으로 저장되어야 합니다. 일반적인 선택 사항은 다음과 같습니다.
- Redis: 만료 포함 키 저장에 이상적인 속도와 TTL(Time-To-Live) 기능으로 뛰어납니다.
- 데이터베이스(예: PostgreSQL, MySQL): 전용 테이블에 키, 요청 매개변수, 응답 데이터 및 타임스탬프를 저장할 수 있습니다. 이는 더 강력한 일관성을 제공하지만 고부하 시나리오에서는 Redis보다 느릴 수 있습니다.
코드 예시 (개념 - Python Flask와 Redis 사용)
Redis를 멱등성 키 저장에 사용하는 개념적인 Python Flask 예시로 설명해 보겠습니다.
import uuid import json from flask import Flask, request, jsonify, make_response import redis import time app = Flask(__name__) # Redis에 연결 redis_client = redis.StrictRedis(host='localhost', port=6379, db=0) # 멱등성 키 TTL (예: 24시간) IDEMPOTENCY_KEY_TTL_SECONDS = 24 * 60 * 60 @app.route('/api/orders', methods=['POST']) def create_order(): idempotency_key = request.headers.get('Idempotency-Key') if not idempotency_key: return jsonify({

