Skip to main content

개요

멱등성(Idempotency)은 동일한 요청을 여러 번 보내도 결과가 동일하게 유지되는 특성입니다. 네트워크 오류나 타임아웃으로 인한 재시도 시 중복 작업을 방지합니다.

중복 방지

동일한 운세가 두 번 생성되는 것을 방지

안전한 재시도

타임아웃 시 안심하고 재시도 가능

사용 방법

Idempotency-Key 헤더

Idempotency-Key 헤더에 고유한 값을 포함하여 요청합니다:
curl -X POST https://api.sajuapi.dev/v1/fortunes \
  -H "X-API-Key: bs_live_xxx" \
  -H "Idempotency-Key: fortune-2025-01-15-prf_abc123-daily" \
  -H "Content-Type: application/json" \
  -d '{
    "profile_id": "prf_abc123",
    "type": "daily"
  }'

키 생성 권장 방식

// 권장: 의미 있는 조합으로 생성
const idempotencyKey = `${operation}-${date}-${profileId}-${type}`;
// 예: "fortune-2025-01-15-prf_abc123-daily"

// 또는 UUID 사용
const idempotencyKey = crypto.randomUUID();
// 예: "550e8400-e29b-41d4-a716-446655440000"

동작 방식

첫 번째 요청

Client                          Server
  │                               │
  │  POST /v1/fortunes            │
  │  Idempotency-Key: xxx         │
  │ ─────────────────────────────►│
  │                               │
  │                               │  처리 및 결과 저장
  │                               │
  │  200 OK                       │
  │  { fortune data }             │
  │ ◄─────────────────────────────│

동일 키로 재요청

Client                          Server
  │                               │
  │  POST /v1/fortunes            │
  │  Idempotency-Key: xxx         │
  │ ─────────────────────────────►│
  │                               │
  │                               │  저장된 결과 반환
  │                               │  (재처리 없음)
  │                               │
  │  200 OK                       │
  │  Idempotent-Replayed: true    │
  │  { 동일한 fortune data }       │
  │ ◄─────────────────────────────│

응답 헤더

첫 번째 요청

HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_abc123

재시도 요청 (멱등성 적용)

HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_def456
Idempotent-Replayed: true
Idempotent-Replayed: true 헤더가 있으면 이전 결과가 재사용된 것입니다.

키 요구사항

항목요구사항
길이10-64자
문자영문, 숫자, 하이픈, 언더스코어
유효 기간24시간
대소문자구분함

유효한 키 예시

fortune-2025-01-15-prf_abc123
550e8400-e29b-41d4-a716-446655440000
create-profile-user12345-20250115

무효한 키 예시

abc         # 너무 짧음 (10자 미만)
key with spaces  # 공백 포함
키-한글      # 한글 포함

지원 엔드포인트

멱등성 키는 상태를 변경하는 (POST, PUT, DELETE) 엔드포인트에서 지원됩니다:
엔드포인트멱등성 지원
POST /v1/profiles
PUT /v1/profiles/
DELETE /v1/profiles/
POST /v1/fortunes
POST /v1/fortunes/batch
DELETE /v1/fortunes/
POST /v1/webhooks
DELETE /v1/webhooks/
GET /v1/*- (GET은 기본적으로 멱등)

에러 처리

키 재사용 오류 (다른 요청)

동일한 키로 다른 요청을 보내면 409 오류가 발생합니다:
{
  "status": 409,
  "type": "/errors/conflict",
  "title": "Conflict",
  "detail": "Idempotency key already used for a different request",
  "instance": "/v1/fortunes"
}

처리 중 재요청

이전 요청이 아직 처리 중일 때 같은 키로 재요청하면:
{
  "status": 409,
  "type": "/errors/conflict",
  "title": "Conflict",
  "detail": "Previous request with this idempotency key is still in progress",
  "retry_after": 5
}

유효하지 않은 키

{
  "status": 400,
  "type": "/errors/validation",
  "title": "Bad Request",
  "detail": "Idempotency key must be 10-64 characters long"
}

구현 예시

JavaScript/TypeScript

import { v4 as uuidv4 } from 'uuid';

class SajuClient {
  private apiKey: string;
  private baseUrl = 'https://api.sajuapi.dev';

  async createFortune(profileId: string, options?: {
    type?: string;
    model?: string;
    idempotencyKey?: string;
  }) {
    const idempotencyKey = options?.idempotencyKey ||
      `fortune-${new Date().toISOString().split('T')[0]}-${profileId}`;

    const response = await fetch(`${this.baseUrl}/v1/fortunes`, {
      method: 'POST',
      headers: {
        'X-API-Key': this.apiKey,
        'Content-Type': 'application/json',
        'Idempotency-Key': idempotencyKey
      },
      body: JSON.stringify({
        profile_id: profileId,
        type: options?.type || 'daily',
        model: options?.model || 'haiku'
      })
    });

    const isReplayed = response.headers.get('Idempotent-Replayed') === 'true';
    const data = await response.json();

    return { data, isReplayed };
  }
}

Python

import uuid
from datetime import date

class SajuClient:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://api.sajuapi.dev"

    def create_fortune(
        self,
        profile_id: str,
        fortune_type: str = "daily",
        idempotency_key: str = None
    ):
        if idempotency_key is None:
            idempotency_key = f"fortune-{date.today()}-{profile_id}"

        response = requests.post(
            f"{self.base_url}/v1/fortunes",
            headers={
                "X-API-Key": self.api_key,
                "Idempotency-Key": idempotency_key
            },
            json={
                "profile_id": profile_id,
                "type": fortune_type
            }
        )

        is_replayed = response.headers.get("Idempotent-Replayed") == "true"
        return response.json(), is_replayed

권장사항

의미 있는 키 사용

작업 내용을 반영하는 키를 사용하면 디버깅이 쉽습니다. 예: create-profile-user123-20250115

클라이언트에서 생성

서버가 아닌 클라이언트에서 키를 생성해야 재시도 시 동일한 키를 사용할 수 있습니다.

키 저장

재시도가 필요한 경우를 대비해 요청과 함께 키를 저장해 두세요.

24시간 제한 인지

키는 24시간 후 만료됩니다. 장기 작업은 새로운 키를 사용하세요.