FastAPI 오래 걸리는 작업: 202와 Job ID로 응답하기

FastAPI에서 오래 걸리는 작업은 요청을 열어 둔 채 기다리지 말고 Job ID와 함께 202로 응답하세요. AI 영상 Job이라면 작업은 Sume가 하고 결과는 웹훅으로 옵니다.

읽는 시간 5분Sume
전체 글

FastAPI에서 오래 걸리는 작업은 작업이 끝날 때까지 요청을 열어 두지 마세요. 요청을 받으면 Job ID와 함께 202 Accepted로 응답하고, 작업은 요청 밖에서 실행하며, 클라이언트는 상태 라우트를 확인하거나 웹훅을 받게 하세요. 그 오래 걸리는 작업이 Sume API로 만드는 AI 영상이라면 작업은 이미 Sume 쪽에서 실행됩니다. 엔드포인트는 Job을 제출하고 그 ID를 저장한 뒤 반환하면 되며, BackgroundTasks 안의 루프나 Celery 워커가 영상을 기다릴 필요가 없습니다.

FastAPI 관련 내용은 FastAPI의 백그라운드 작업, 응답 상태 코드, 동시성과 async / await 페이지에서, Sume 관련 내용은 영상 생성 (영문), Job과 결과 (영문), 웹훅 (영문)에서 가져왔습니다. 모두 2026-09-27에 확인했습니다. Sume에는 FastAPI용 패키지가 없으며, 엔드포인트는 Requests로 일반 HTTPS 호출을 보냅니다.

오래 걸리는 작업에는 BackgroundTasks와 Celery 중 무엇을 써야 하나요?

작업이 어디서 실행되느냐에 따라 다릅니다. FastAPI 문서는 BackgroundTasks를 응답을 반환한 뒤 실행되는 작업으로 설명하며, 202 Accepted로 응답하고 느린 파일은 백그라운드에서 처리하는 방법까지 제안합니다. 같은 프로세스가 필요 없는 무거운 연산에는 Celery 같은 더 큰 도구를 권합니다. 이런 도구는 대개 RabbitMQ나 Redis 같은 메시지 큐 또는 작업 큐 관리자가 필요하지만, 작업을 여러 프로세스와 여러 서버에서 실행할 수 있습니다.

Sume 영상은 어느 쪽에도 맞지 않습니다. Sume 문서에 따르면 영상 생성은 보통 30초에서 몇 분이 걸리며, 그 전부가 Sume 쪽에서 진행됩니다. BackgroundTasks 안의 폴링 루프는 그동안 내내 웹 앱 안에 머물고, Celery 워커는 기다림을 다른 곳으로 옮길 뿐입니다.

FastAPI의 백그라운드 작업 페이지와 Sume의 영상 생성 (영문) 문서 기준, 2026-09-27 확인.
방법작업이 실행되는 곳적합한 작업
BackgroundTasksFastAPI 앱 안, 응답을 보낸 뒤이메일 알림 같은 작은 작업, 또는 앱 자체의 변수와 객체가 필요한 작업
큐를 쓰는 CeleryRabbitMQ나 Redis에서 작업을 받는 워커 프로세스. 다른 서버에 있을 수도 있음직접 실행하는 무거운 연산
POST /v1/videos 같은 비동기 APISume 쪽. 제출하면 Job ID와 폴링 URL이 바로 돌아옴시작하고 결과만 받으면 되는 작업

FastAPI 엔드포인트에서 영상 Job은 어떻게 시작하나요?

제출하고, 저장하고, 반환합니다. 엔드포인트는 데코레이터의 status_code 매개변수로 자체 202를 설정합니다. 함수는 일반 def입니다. API를 호출하는 라이브러리가 await를 지원하지 않을 때 FastAPI 문서가 권하는 방식이며, FastAPI는 이런 경로 작동을 서버를 막는 대신 외부 스레드풀에서 실행합니다. Sume는 제출에 202와 감싸지 않은 객체로 응답하며, 이 객체에는 id, polling_url, status: "pending", model이 담깁니다.

  • Idempotency-Key는 요청이 아니라 여러분의 레코드에서 만듭니다. 같은 키를 같은 본문과 함께 다시 보내면 두 번째 유료 Job 대신 원래 Job이 돌아옵니다.
  • callback_url은 공개 HTTPS URL이어야 하며, Job이 종료 상태에 도달하면 Sume가 그 URL로 POST합니다. localhost와 사설 네트워크 URL은 거부됩니다.
  • Requests 문서는 application/json Content-Type이 필요할 때 json=을 쓰라고 안내하며, timeout이 없는 호출은 무기한 멈출 수 있다고 경고합니다.
  • SUME_API_KEY는 서버 환경에만 두고, 프런트엔드 JavaScript에는 절대 넣지 마세요.
import os, requests
from fastapi import FastAPI

app = FastAPI()

@app.post("/orders/{order_id}/video", status_code=202)
def start_video(order_id: str):  # plain def: runs in FastAPI's threadpool
    if job := db.find_video_job(order_id):  # your table: already submitted
        return job
    r = requests.post(
        "https://api.sume.com/v1/videos",
        headers={
            "Authorization": f"Bearer {os.environ['SUME_API_KEY']}",
            "Idempotency-Key": f"order-{order_id}-video-v1",  # from the record
        },
        json={
            "model": "sume/auto",
            "prompt": db.video_prompt(order_id),  # same record, same body
            "callback_url": "https://example.com/hooks/sume",
        },
        timeout=30,
    )
    r.raise_for_status()
    job = r.json()  # id, polling_url, status: "pending"
    db.save_video_job(order_id, job["id"], job["polling_url"])
    return {"job_id": job["id"], "status": job["status"]}

요청이 타임아웃되거나 서버가 죽으면 어떻게 되나요?

Job ID나 키가 남아 있는 한 잃는 것은 없습니다. 제출은 Sume가 내구성 있는 Job ID를 확보한 순간 수락되며, 문서는 프로세스가 재시작된 뒤에도 연동 코드가 작업을 복구할 수 있도록 그 ID를 저장하라고 안내합니다. 2xx는 Job이 존재하고 유료 작업이 진행 중이라는 뜻이지, 작업이 끝났다는 뜻이 아닙니다.

  • ID를 저장하기 전에 죽은 경우: 같은 Idempotency-Key와 같은 본문으로 다시 제출하면 재전송으로 원래 Job이 돌아옵니다. 같은 키로 다른 본문을 보내면 409 idempotency_conflict이므로, 프롬프트도 레코드에서 만드세요.
  • 직접 보낸 호출이 타임아웃된 경우: 클라이언트 쪽 타임아웃은 Job을 취소하지 않습니다. Job은 계속 실행되고 여전히 과금되므로, 새 유료 요청을 제출하지 말고 그 Job을 다시 읽으세요.
  • 워커가 재시작된 경우: 저장해 둔 Job ID가 있으면 어느 프로세스든 GET /v1/jobs/{id}/status에서 그 Job을 다시 이어받을 수 있습니다.

완성된 영상은 어떻게 내 앱으로 돌아오나요?

callback_url을 통해 돌아옵니다. Sume는 종료 Job 이벤트인 job.completed, job.failed, job.canceled만 보내며, 각 이벤트는 <timestamp>.<raw_body>에 대한 HMAC-SHA256으로 서명됩니다. job.completed 페이로드의 artifacts에는 media.sume.com URL이 나열됩니다. 원본 본문은 파싱하기 전에 검증하세요. 그 라우트는 FastAPI·Django에서 Python 웹훅 HMAC 검증하기에 있습니다.

  • 이벤트를 내구성 있게 저장한 뒤 2xx로 응답하세요. 어떤 2xx든 됩니다. 시도마다 10초가 주어지며, Sume는 최대 10회 시도합니다.
  • 여러분 쪽의 멱등성 키로는 job_id를 쓰세요. 그래야 같은 전달이 반복돼도 한 행만 갱신됩니다.
  • 도착하지 않는 전달에 대비해 폴링을 백업으로 유지하세요. 폴링 루프와 다운로드는 Python Text-to-Video API에서 다룹니다.

출처

관련 글

연동 카테고리의 다른 글

연동 글 전체 보기

작성자 Sume