//////

Core API Long-Running Operation Contracts: 202 Accepted Status Resources, Polling/Backoff, Cancellation, and Idempotent Submission Failure Modes

Core API에서 시간이 오래 걸리는 작업(long-running operation, LRO)은 동기 POST/PATCH 응답으로 완료 결과를 즉시 반환하기보다, 202 Accepted와 함께 상태 리소스(status resource / operation resource) 를 반환하는 계약으로 분리하는 것이 안전하다. 핵심은 “요청은 접수되었지만 아직 완료되지 않았다”는 사실을 명확히 표현하고, 클라이언트가 상태 조회, 백오프 기반 폴링, 취소 요청, 재시도

//////

Summary#

Core API에서 시간이 오래 걸리는 작업(long-running operation, LRO)은 동기 POST/PATCH 응답으로 완료 결과를 즉시 반환하기보다, 202 Accepted와 함께 상태 리소스(status resource / operation resource) 를 반환하는 계약으로 분리하는 것이 안전하다. 핵심은 “요청은 접수되었지만 아직 완료되지 않았다”는 사실을 명확히 표현하고, 클라이언트가 상태 조회, 백오프 기반 폴링, 취소 요청, 재시도 및 중복 제출 처리까지 예측 가능하게 수행하도록 만드는 것이다.

권장 계약은 다음 구조다.

  • 최초 제출:
  • 성공적으로 접수되면 202 Accepted
  • Location 또는 Operation-Location 헤더로 상태 리소스 URL 제공
  • 가능하면 Retry-After로 최소 재조회 간격 제공
  • 응답 본문에도 operation id, status, timestamps, links 제공
  • 상태 조회:
  • GET /operations/{operation_id}
  • pending, running, succeeded, failed, canceling, canceled 등 명시적 상태
  • 완료 시 결과 리소스 링크 또는 에러 객체 제공
  • 폴링:
  • 서버의 Retry-After를 우선 따름
  • 없으면 지수 백오프와 jitter 사용
  • 클라이언트는 무한 tight polling 금지
  • 취소:
  • POST /operations/{operation_id}:cancel 또는 DELETE /operations/{operation_id} 중 하나를 일관되게 선택
  • 취소는 “요청됨” 상태일 수 있으며 즉시 완료를 보장하지 않음
  • 멱등 제출:
  • Idempotency-Key 또는 클라이언트 생성 request id 사용
  • 동일 키 재시도는 같은 operation을 반환하거나 이전 결과와 일관된 응답을 반환
  • 네트워크 타임아웃, 5xx, 클라이언트 재전송에서 중복 작업 생성을 방지해야 함

Key Points#

1. 202 Accepted는 완료가 아니라 “접수” 의미다#

HTTP 202 Accepted는 요청이 처리 대상으로 받아들여졌지만 아직 완료되지 않았음을 나타낸다. 따라서 202 응답을 반환하면서 실제 작업 결과가 이미 확정된 것처럼 표현하면 안 된다.

권장 응답 예시:

HTTP/1.1 202 Accepted
Location: /operations/op_123
Retry-After: 10
Content-Type: application/json
{
  "id": "op_123",
  "status": "pending",
  "created_at": "2026-07-27T12:00:00Z",
  "updated_at": "2026-07-27T12:00:00Z",
  "links": {
    "self": "/operations/op_123",
    "cancel": "/operations/op_123:cancel"
  }
}

Location 또는 Operation-Location 중 무엇을 쓸지는 API 스타일에 따라 달라질 수 있다. 중요한 점은 클라이언트가 어디를 조회해야 하는지를 기계적으로 알 수 있어야 한다는 것이다.

2. 상태 리소스는 LRO 계약의 중심이다#

상태 리소스는 단순한 내부 job id 노출이 아니라 공개 API 계약이다. 최소한 다음 필드를 포함하는 것이 좋다.

{
  "id": "op_123",
  "status": "running",
  "percent_complete": 45,
  "created_at": "2026-07-27T12:00:00Z",
  "updated_at": "2026-07-27T12:01:20Z",
  "started_at": "2026-07-27T12:00:05Z",
  "expires_at": "2026-07-28T12:00:00Z",
  "links": {
    "self": "/operations/op_123",
    "result": null,
    "cancel": "/operations/op_123:cancel"
  }
}

완료 시:

{
  "id": "op_123",
  "status": "succeeded",
  "created_at": "2026-07-27T12:00:00Z",
  "updated_at": "2026-07-27T12:05:00Z",
  "completed_at": "2026-07-27T12:05:00Z",
  "result": {
    "resource_type": "capsule",
    "resource_id": "cap_789"
  },
  "links": {
    "self": "/operations/op_123",
    "result": "/capsules/cap_789"
  }
}

실패 시:

{
  "id": "op_123",
  "status": "failed",
  "error": {
    "code": "VALIDATION_FAILED_AFTER_ACCEPTANCE",
    "message": "The submitted payload could not be normalized.",
    "retryable": false
  }
}

상태 값은 확장 가능하게 설계하되, 클라이언트가 반드시 처리해야 하는 terminal state와 non-terminal state를 문서화해야 한다.

권장 상태 분류:

  • Non-terminal:
  • pending
  • running
  • canceling
  • Terminal:
  • succeeded
  • failed
  • canceled
  • 선택적으로 expired

3. 폴링은 서버 힌트와 클라이언트 백오프를 함께 사용한다#

클라이언트는 상태 리소스를 짧은 간격으로 계속 조회하면 안 된다. 서버가 Retry-After를 제공하면 이를 우선 따라야 한다.

권장 순서:

  1. 202 또는 상태 조회 응답의 Retry-After 확인
  2. 있으면 해당 시간 이후 재조회
  3. 없으면 지수 백오프 사용
  4. 여러 클라이언트 동시 재시도 방지를 위해 jitter 적용
  5. 일정 시간 이후 사용자에게 “아직 처리 중” 상태 표시
  6. 클라이언트 자체 timeout과 서버 operation expiration을 구분

예시 정책:

initial_delay = 1s
max_delay = 30s
multiplier = 2
jitter = random(0.8, 1.2)
overall_client_timeout = caller-defined

서버는 폴링을 줄이기 위해 다음을 제공할 수 있다.

  • Retry-After
  • progress 정보
  • webhook callback 등록
  • event stream 또는 notification channel
  • operation expiration time

다만 webhook은 폴링을 완전히 대체하기보다 reconciliation을 위한 상태 조회와 함께 제공하는 편이 안전하다.

4. 취소는 “best effort” 계약으로 다루는 것이 현실적이다#

LRO 취소는 보통 즉시 중단을 보장하기 어렵다. 이미 커밋된 외부 부작용, 큐에서 실행 중인 작업, 부분 완료 상태가 있을 수 있기 때문이다.

권장 취소 엔드포인트:

POST /operations/op_123:cancel

응답 예시:

HTTP/1.1 202 Accepted
Location: /operations/op_123
{
  "id": "op_123",
  "status": "canceling"
}

취소 결과:

{
  "id": "op_123",
  "status": "canceled",
  "completed_at": "2026-07-27T12:03:00Z"
}

또는 이미 완료된 경우:

HTTP/1.1 409 Conflict
{
  "error": {
    "code": "OPERATION_ALREADY_COMPLETED",
    "message": "The operation has already reached terminal state succeeded."
  }
}

취소 계약에서 명시해야 할 내용:

  • 취소 가능 상태
  • 취소 요청 후 상태 전이
  • 부분 완료 작업의 보상/rollback 여부
  • 취소 불가능한 terminal state
  • 취소 요청 자체의 멱등성

5. 멱등 제출은 LRO에서 특히 중요하다#

LRO 제출은 네트워크 단절, gateway timeout, 클라이언트 재시도, 서버 5xx로 인해 “요청이 접수되었는지 알 수 없는” 상황이 자주 발생한다. 이때 멱등 키가 없으면 같은 작업이 여러 번 생성될 수 있다.

권장 방식:

POST /capsule-imports
Idempotency-Key: 01J4ABCDEF...
Content-Type: application/json

동일 Idempotency-Key와 동일 요청 본문이 다시 들어오면:

HTTP/1.1 202 Accepted
Location: /operations/op_123

이미 완료된 경우에도 기존 operation 또는 결과를 일관되게 반환한다.

HTTP/1.1 200 OK
{
  "id": "op_123",
  "status": "succeeded",
  "links": {
    "result": "/capsules/cap_789"
  }
}

동일 키지만 다른 본문이면 충돌로 처리한다.

HTTP/1.1 409 Conflict
{
  "error": {
    "code": "IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_PAYLOAD",
    "message": "This idempotency key was previously used with a different request payload."
  }
}

6. 주요 실패 모드와 권장 처리#

제출 요청이 타임아웃됨

상황:

  • 클라이언트가 POST를 보냈다.
  • 응답을 받기 전에 연결이 끊겼다.
  • 서버가 실제로 operation을 만들었는지 알 수 없다.

권장 처리:

  • 클라이언트는 동일 Idempotency-Key로 재시도
  • 서버는 기존 operation이 있으면 동일 operation 반환
  • 없으면 새 operation 생성

202 Accepted 이후 작업 실패

상황:

  • 요청 형식은 접수 가능했지만, 비동기 처리 중 실패

권장 처리:

  • 상태 리소스의 status = failed
  • machine-readable error code 제공
  • retryable 여부 제공
  • 원 요청 전체를 다시 보내야 하는지, 새 operation을 만들어야 하는지 문서화

상태 리소스가 만료됨

상황:

  • operation 조회 보존 기간이 지남

권장 처리:

  • 404 Not Found 또는 410 Gone 중 하나를 정책화
  • 가능하면 결과 리소스가 존재하면 result URL로 유도
  • operation retention 기간을 문서화

중복 작업이 이미 실행 중

상황:

  • 동일 의미의 작업을 다른 idempotency key로 여러 번 제출

권장 처리:

  • 비즈니스 키 기준 deduplication이 필요한지 별도 설계
  • 단순 idempotency key만으로는 semantic duplicate를 막지 못함
  • 필요하면 409 Conflict와 기존 operation link 제공

webhook 전달 실패

상황:

  • 작업은 완료되었지만 callback delivery 실패

권장 처리:

  • webhook은 at-least-once로 문서화
  • 이벤트 payload에 operation id와 event id 포함
  • 수신자는 중복 이벤트를 멱등 처리
  • 클라이언트는 최종적으로 GET /operations/{id}로 reconciliation

7. Core API에 적용할 수 있는 최소 계약 초안#

권장 엔드포인트:

POST /operations-producing-resource
GET  /operations/{operation_id}
POST /operations/{operation_id}:cancel

권장 헤더:

Idempotency-Key
Location 또는 Operation-Location
Retry-After

권장 operation schema:

{
  "id": "string",
  "status": "pending | running | succeeded | failed | canceling | canceled",
  "created_at": "datetime",
  "updated_at": "datetime",
  "completed_at": "datetime | null",
  "expires_at": "datetime | null",
  "progress": {
    "percent": 0,
    "message": "string"
  },
  "result": {
    "resource_type": "string",
    "resource_id": "string"
  },
  "error": {
    "code": "string",
    "message": "string",
    "retryable": true
  },
  "links": {
    "self": "string",
    "result": "string",
    "cancel": "string"
  }
}

권장 문서화 항목:

  • 어떤 요청이 LRO를 반환하는가
  • 최초 응답 status code
  • operation 상태 전이
  • polling interval 규칙
  • Retry-After 의미
  • operation 보존 기간
  • cancellation semantics
  • idempotency key retention period
  • retryable failure와 non-retryable failure
  • webhook 사용 시 reconciliation 방법

Cautions#

  • 이 환경에는 사용자가 요청한 WebSearch/WebFetch 도구가 제공되지 않아, 실시간 공개 웹 검색 및 fetch 검증을 수행하지 못했다. 아래 출처는 공개적으로 알려진 표준/벤더 문서 URL을 기반으로 한 초안 출처다.
  • Operation-Location 헤더는 모든 API 표준에서 공통으로 강제되는 헤더가 아니다. Azure 계열 비동기 패턴에서는 자주 보이지만, 일반 REST API에서는 Location과 응답 본문 link를 사용하는 설계도 흔하다.
  • 202 Accepted 이후 실제 작업이 반드시 성공한다는 의미는 없다. 접수 후 실패를 상태 리소스에 명확히 기록해야 한다.
  • 취소는 즉시 중단 보장이 아니라 best-effort로 정의하는 것이 안전하다. 이미 완료되었거나 외부 side effect가 발생한 작업은 취소할 수 없을 수 있다.
  • webhook은 폴링을 완전히 대체하지 않는다. delivery 실패, 중복 전달, 순서 역전이 가능하므로 operation status endpoint를 통한 reconciliation이 필요하다.
  • idempotency key만으로 semantic duplicate를 전부 막을 수는 없다. 같은 의도의 요청이 서로 다른 key로 들어오는 경우에는 별도의 business-level deduplication이 필요하다.
  • operation status retention 기간이 짧으면 클라이언트 장애 복구가 어려워질 수 있다. retention, expiration, 404/410 정책을 명시해야 한다.

Sources#

  • https://www.rfc-editor.org/rfc/rfc9110.html
  • https://learn.microsoft.com/en-us/azure/architecture/patterns/async-request-reply
  • https://cloud.google.com/apis/design/design_patterns#long_running_operations
  • https://google.aip.dev/151

Sagwan Revalidation 2026-07-28T00:05:34Z#

  • verdict: ok
  • note: 202 상태 리소스·백오프·취소·멱등 키 권장안은 현재도 유효하다.

Sagwan Revalidation 2026-07-30T04:13:25Z#

  • verdict: ok
  • note: 202 LRO, 폴링/취소/멱등 처리 권장은 최신 관행과 부합함

Sagwan Revalidation 2026-08-01T15:00:32Z#

  • verdict: ok
  • note: 202 LRO, 폴링/취소/멱등성 권장안은 현재도 표준적이다.

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
1