OpenAI Responses API 백그라운드 실행, 폴링·취소·재시도를 안전하게 설계하는 법

무브랜드 서버 랙 후면의 환기 그릴과 금속 패널이 보이는 현실적인 장비실 장면

AI를 활용하여 생성한 이미지입니다.

긴 보고서 작성, 배치 분석, 대규모 코드 변환처럼 응답이 오래 걸리는 작업은 일반적인 동기 요청으로 붙잡아 두기 어렵습니다. OpenAI Responses API는 background=true로 응답 생성을 비동기로 시작하고, 반환된 response 객체를 다시 조회해 상태를 확인하는 방식을 제공합니다.[1][2] 이 기능을 붙일 때 중요한 건 “요청을 오래 기다리지 않는다”가 아니라 작업의 상태와 재시도 기준을 애플리케이션이 직접 갖는다는 점이에요.

background 요청은 큐가 아니에요

백그라운드 모드는 요청을 시작하고 나중에 조회할 수 있게 해 주지만, 여러분의 서비스에 자동 작업 큐나 중복 방지 정책을 만들어 주지는 않아요. 요청을 보낸 뒤 반환된 response ID, 내부 작업 ID, 사용자 요청자, 프롬프트 버전을 함께 저장해야 합니다. 같은 사용자가 새로고침해서 같은 작업을 다시 만들었을 때도 이 기록이 있어야 중복 실행을 막을 수 있어요.[1]

상태 관리 항목 왜 필요한가 저장할 값
작업 식별자 재시도와 중복 조회를 구분해요. 내부 job_id, OpenAI response_id
상태 완료·실패·취소를 나눠요. queued, in_progress, completed 등
입력 버전 결과가 어떤 지시에서 나왔는지 남겨요. 프롬프트 해시, 모델, 도구 설정
결과 위치 폴링이 끝난 뒤 재다운로드를 피하게 해요. 저장된 응답 ID, 결과 요약, 저장 시각

폴링 간격은 고정값보다 백오프가 낫습니다

공식 예시는 response 객체를 다시 조회하는 흐름을 보여줘요.[1] 실제 운영에서는 처음부터 1초마다 조회하지 말고 짧은 작업과 긴 작업을 구분하는 편이 좋아요. 예를 들어 첫 조회는 몇 초 뒤에 하고, 그 다음부터 5초·10초·20초처럼 간격을 늘리되 최대 간격을 정해 두세요. 완료 상태가 되면 즉시 멈추고, 일정 시간 동안 상태가 그대로면 네트워크 오류와 모델 지연을 분리해 기록합니다.

POST /v1/responses
{
  "model": "o3",
  "input": "긴 분석 작업",
  "background": true
}

GET /v1/responses/{response_id}

폴링 요청이 실패했다고 원래 생성 작업이 실패한 것은 아니에요. 조회만 타임아웃됐을 수 있으므로 내부 작업을 바로 재실행하지 말고 같은 response ID를 다시 조회하세요. 반대로 응답 객체가 실패 상태를 반환했다면 입력·도구·권한 문제를 확인한 뒤 새로운 시도에 새로운 idempotency 키를 부여하는 흐름이 안전합니다.

취소는 별도의 상태 전이예요

진행 중인 background response는 취소 API로 중단할 수 있습니다.[1] 사용자가 취소 버튼을 눌렀다면 UI만 숨기지 말고 내부 작업을 cancel_requested로 바꾼 뒤 취소 요청을 보내세요. 이후 response를 다시 읽어 실제 취소 상태가 되었는지 확인해야 합니다. 취소 요청과 취소 완료 사이에는 짧은 경주 구간이 있을 수 있으므로, 그 사이에 완료 결과가 저장될 수 있다는 점도 처리해야 해요.

취소된 작업을 자동 재시작하면 사용자의 의도와 반대가 됩니다. 재시작 버튼을 별도로 두고, 재시작할 때는 원본 입력의 버전과 새 작업의 생성 시각을 분리해 저장하세요. 실패와 취소를 같은 “에러”로 합치면 운영자가 원인을 잘못 판단합니다.

스트리밍과 폴링 중 무엇을 고를까?

배경 응답은 폴링으로 상태를 조회할 수 있고, 스트리밍을 시작한 뒤 나중에 이어받는 방식도 문서에 나와 있어요.[1] UI에서 중간 진행 상황을 보여줘야 한다면 스트리밍이 매력적이지만, 연결이 끊긴 뒤 재개 지점과 중복 이벤트를 처리해야 합니다. 결과만 필요하다면 폴링이 단순해요. 둘을 섞을 때는 화면에 표시한 이벤트와 최종 저장한 결과를 별도의 데이터로 취급하세요.

상황 권장 방식 주의점
관리자 배치 분석 폴링 후 결과 저장 중복 실행 잠금과 백오프
사용자에게 진행 표시 스트리밍 또는 주기 조회 끊긴 연결의 재개 위치
긴 작업을 중단해야 함 취소 API와 상태 재조회 취소와 완료의 경주

재시도 기준을 먼저 고정해요

네트워크 502나 일시적인 연결 실패는 같은 response ID를 조회하는 재시도 대상일 수 있어요. 하지만 입력이 잘못됐거나 모델이 요청을 거부한 상태는 같은 요청을 무한히 재전송해도 해결되지 않습니다. 내부적으로는 조회 실패, 생성 실패, 사용자 취소, 제한 시간 초과를 각각 다른 코드로 남기세요.

  1. 응답 생성 직후 response ID와 내부 작업 ID를 저장해요.
  2. 상태 조회는 백오프하고, 조회 실패와 생성 실패를 분리해요.
  3. 완료 상태가 되면 결과를 한 번 저장하고 중복 저장을 막아요.
  4. 취소 버튼은 취소 요청과 최종 상태 재조회를 함께 실행해요.
  5. 시간 제한에 걸려도 즉시 재생성하지 말고 같은 response를 먼저 확인해요.

데이터 보관 조건도 설계에 포함해요

백그라운드 모드는 응답 상태를 나중에 조회하는 기능이지만, 데이터 보관 요구사항과 별개로 검토해야 합니다. OpenAI의 데이터 제어 문서는 background mode가 폴링을 위해 응답 데이터를 일정 시간 디스크에 보관한다고 설명합니다.[3] 민감한 입력을 다루는 서비스라면 조직의 보관 정책, Zero Data Retention 조건, 로그에 남기는 프롬프트 범위를 함께 확인해야 해요.

운영자가 확인할 최소 기록은 response ID, 상태 변화 시각, 모델, 입력 버전, 오류 분류, 최종 결과 저장 여부예요. 원문 프롬프트와 결과 전체를 내부 로그에 남기는 것이 필요한지는 별도로 판단하세요. 필요한 데이터만 남겨야 복구도 쉽고 노출 면적도 줄어듭니다.

검증 체크리스트

  • 같은 내부 작업이 두 번 실행되지 않는지 확인해요.
  • 폴링 백오프와 최대 대기 시간을 정해요.
  • 조회 오류와 모델 실패를 별도 상태로 기록해요.
  • 취소 후 완료 결과가 저장되는 경주를 처리해요.
  • 스트리밍 재개 시 이벤트 중복을 제거해요.
  • 보관·삭제 정책과 결과 저장 위치를 문서화해요.

이미 Structured Outputs를 쓰고 있다면

긴 비동기 작업의 결과를 JSON으로 저장한다면 완료 후 스키마 검증을 다시 수행해야 해요. 응답 상태가 completed인 것과 여러분의 업무 결과가 유효한 것은 다른 조건입니다. 기존의 Structured Outputs 검증 글처럼 거부 응답과 필드 누락을 분리해 처리하면, “완료됐지만 사용할 수 없는 결과”를 줄일 수 있습니다.

Sources

  1. Background mode | OpenAI API
  2. Create a model response | OpenAI API Reference
  3. Data controls in the OpenAI platform

함께 읽을 글