← 블로그

API로 Seedance 2.0 사용하기: 비동기 작업, 재시도 및 결과 처리

Seedance 2.0 API를 위한 프로덕션 패턴: 비동기 작업 생명주기, 재시도, 멱등성, 관찰 가능성 및 비용 제한.

7 min read
API로 Seedance 2.0 사용하기: 비동기 작업, 재시도 및 결과 처리

Seedance 2.0처럼 영화 같은 영상을 만들고 싶으신가요? WaveSpeed Cinematic Video Generator를 사용해 지금 바로 Seedance 2.0 수준의 영화 같은 영상을 만들어 보세요.

안녕하세요, 여러분. Dora입니다. 저는 Seedance 2.0 API에서 오래 실행되는 작업을 계속 건드리면서, 작업이 끝났는지 확인하려고 alt-tab을 반복하는 저 자신을 발견하곤 했습니다. 시스템이 망가진 게 아니라, 그냥 은근히 피로한 일이었죠. 며칠에 걸쳐 실제 작업(콘텐츠 변환 및 배치 추출)을 몇 가지 돌려보면서, 실제로 하루의 흐름을 바꿔준 부분들에 주목했습니다.

이 글은 작업을 더 안정적으로 만들어 준 몇 가지 패턴에 대한 이야기입니다. 제출, 추적, 결과 수집 방법, 입력값 패키징 방법, 재시도할 것과 하지 않을 것, 그리고 키, 비용, 로그에서 실수하지 않기 위한 기본 안전장치들입니다. 이미 API를 다루고 있다면 익숙하게 느껴질 것이고, 그건 의도된 것입니다.

API 작업 생명주기 (제출 → 상태 확인 → 결과 수집)

저는 Seedance 2.0 API를 머릿속에서 단순하게 유지하려 했습니다. 세 가지 동작, 제출, 상태 확인, 결과 수집. 실제로 그렇게 접근하니 정신적 부담이 줄었습니다.

제출: 명확하고 자급자족적인 페이로드와 클라이언트가 생성한 멱등성 키(나중에 자세히 설명)를 함께 작업을 제출합니다. 코드 주석에 제가 “완료”로 간주하는 것을 적어둡니다. 철학적인 얘기가 아니라, 성공의 정확한 형태(예: X, Y, Z 필드를 포함한 JSON, 일치하는 체크섬, 부분 결과 없음)를 명시하는 것입니다.

상태: 상태를 하나의 개념으로 보는 것을 멈췄습니다. 버킷으로 나눕니다:

  • 진행 중 (폴링해도 안전)
  • 차단됨 (내 조치가 필요함, 주로 잘못된 입력)
  • 종료됨 (성공 또는 영구 실패)

이 작은 분류가 확인 방식을 바꿨습니다. 진행 중이면 물러서서 기다립니다. 차단됐으면 입력값을 수정합니다. 종료됐으면 다음으로 넘어갑니다. 중간 상태 레이블을 과잉 해석하지 않습니다.

결과: 작업이 완료되면, 나중에 신뢰할 수 있는 형식으로 출력값을 가져옵니다. 보통 안정적인 스키마와 간단한 콘텐츠 해시가 포함된 JSON입니다. API가 웹훅을 지원해도, 폴링을 폴백으로 유지합니다. 웹훅은 훌륭하지만 방화벽 규칙이나 큐 오류로 사라지기도 합니다. 폴링은 지루하지만 신뢰할 수 있습니다.

간단한 현장 메모 두 가지:

  • 초기 실행은 시간을 절약해 주지 않았습니다. 몇 번 반복하고 나서, 주의를 절약해 준다는 것을 알게 됐습니다. “그게 끝났나?” 확인이 줄고, “진짜 끝났을 때 알게 되겠지”가 늘었습니다.
  • 가능하면 API 내부에서 작업을 연쇄하는 것을 피합니다. 하나의 작업, 하나의 결과. 팬아웃이나 의존성 로직이 필요하면 내 시스템에서 처리합니다. 원인 추적과 재시도가 더 깔끔해집니다.

이를 기반으로 구축하고 있다면, 간단한 상태 머신이 도움이 됩니다. 거창할 필요 없이, 몇 가지 열거형 상태와 명확한 전환만 있으면 됩니다. 화려하진 않지만, 엣지 케이스를 스파게티 코드 없이 흡수합니다.

페이로드 설계 (텍스트 + 참조 패키징)

마찰의 대부분은 페이로드에서 왔습니다. 실패가 아니라, 불일치였습니다. 구조를 조금 정리하니 바로 맞아 떨어졌습니다.

필요하지 않을 때는 거대한 텍스트 덩어리를 인라인으로 보내는 것을 멈췄습니다. 대신:

  • 간결한 텍스트 지침과 파라미터는 인라인으로 보냅니다.
  • 대용량 아티팩트(문서, 미디어, 이전 출력값)는 참조로 전달합니다. 서명된 URL이나 객체 키, 버전이 지정된 식별자와 함께.

이 분리 덕분에 재시도가 더 안전해지고 업로드 낭비가 줄었습니다. 로그도 더 명확해졌습니다. 메가바이트 단위의 콘텐츠를 스크롤하지 않고도 실행 간 변경 사항을 확인할 수 있었습니다. Seedance 2.0 API가 텍스트와 참조를 모두 필요로 할 경우, 명확한 이름을 가진 단일 “input” 객체 아래에 둡니다. 미래의 나는 흩어진 필드를 찾아다니지 않아도 된다는 것에 감사할 것입니다.

제출 전 입력값 검증

무언가를 보내기 전에 로컬에서 세 가지를 확인합니다:

  • 형태: 페이로드가 내 스키마와 일치하는가? 필수 필드 존재 여부, 타입 정확성, 열거형 유효성. JSON Schema 검증기를 사용합니다.
  • 참조: URL이 해석되고 크기/타입 규칙을 충족하는가? HEAD 요청으로 사전 확인하고, 가능하면 content-length와 체크섬을 첨부합니다.
  • 기대치: 파라미터가 요청하는 작업의 종류와 일치하는가? “요약”을 요청하면서 “full_transcript=true”를 함께 전달하지 않습니다. 사소한 것처럼 보이지만 실제로 일어납니다.

이런 확인이 오류를 없애주진 않습니다. 가장 저렴한 수정 지점, 즉 네트워크 요청 전, 속도 제한 전, 자정에 로그를 읽기 전으로 옮겨줄 뿐입니다.

신뢰성 패턴

일주일간 꾸준히 사용한 후, 대부분의 골치거리는 이유를 파악할 수 없는 재시도에서 왔습니다. 해결책은 팀원에게 한 문장으로 설명할 수 있는 단순한 패턴이었습니다.

실패를 두 가지로 나눴습니다:

  • 재시도 안전 (일시적인 네트워크 문제, 5xx, 서버 작업 시작 전 타임아웃)
  • 무조건 재시도 금지 (유효성 오류, 할당량 초과, 알 수 없는 상태)

이렇게 하고 나니 나머지가 자연스럽게 정리됐습니다.

멱등성 키 + 안전한 재시도

각 작업 제출에 고유한 멱등성 키를 추가합니다. 서버는 동일한 키로 반복된 요청을 같은 요청으로 처리해야 합니다. 실제로, 요청이 서버에 도달했는지 알 수 없을 수도 있다고 가정합니다. 그래서 재시도를 설계상 안전하게 만듭니다.

도움이 된 것들:

  • 안정적인 입력값(예: UUID와 정규화된 페이로드 해시)에서 키를 파생시켜 우발적인 중복이 의도적으로 충돌하도록 합니다.
  • 키와 의도된 효과를 내 쪽에서 짧은 TTL과 함께 저장합니다. 응답을 잃어버려도 자신 있게 재시도할 수 있습니다.
  • 비멱등 연산(“시작 및 청구”와 같은)을 클라이언트 경계에서 멱등으로 처리합니다. 서버가 강제하거나, 자동 재시도를 피하거나.

좋은 멘탈 모델을 원한다면, 결제 API가 이를 처리하는 방식이 명확합니다. Stripe의 멱등성 키 문서는 간결하고, 돈을 다루지 않더라도 한 번 훑어볼 가치가 있습니다.

타임아웃, 백오프, 재시도 한도

세 가지 숫자를 항상 가까이 둡니다: 요청 타임아웃, 초기 백오프, 최대 시도 횟수.

제 기본 구성은 다음과 같습니다:

  • 타임아웃: 보수적이지만 인색하지 않게. 일반적인 서버 작업에는 충분히 길고, 좀비 소켓을 피하기엔 충분히 짧게. 작업이 진짜 오래 걸린다면, 빠른 제출 호출과 별도의 폴링을 선호합니다.
  • 백오프: 지터(jitter)가 있는 지수적 증가. 지터가 중요합니다. 없으면 동기화된 재시도가 소규모 DDoS처럼 동작합니다.
  • 한도: 총 재시도 횟수와 작업당 총 실시간 제한. 한도에 도달하면 사람이 이해할 수 있는 오류를 표시하고 멈춥니다. 조용한 반복은 없습니다.

실제로 이 숫자는 두 번 바뀌었습니다. 첫날 이후 한 번(너무 공격적이었음), 매시간 초에 짧은 급등 패턴을 발견한 후 한 번(지터를 더 추가했음). 전혀 화려하지 않습니다. 그냥 시스템을 더 조용하게 만들었습니다.

관찰 가능성 (로그, 실패 버킷, 비용 모니터링)

필요하지 않으면 풀 트레이싱은 추구하지 않습니다. Seedance 2.0 API 작업에는 세 가지 뷰로 충분했습니다:

  • 상관 ID가 있는 요청 로그: 각 제출, 상태 확인, 결과 호출에 동일한 상관 ID를 태깅합니다. 뭔가 잘못됐을 때 추측 없이 하나의 작업을 처음부터 끝까지 추적할 수 있습니다. 처음 설정한다면 OpenTelemetry의 시맨틱 컨벤션이 도움이 됩니다.
  • 실패 버킷: 실패를 원인별로 분류합니다(유효성, 인증, 할당량, 타임아웃, 5xx, 스키마 불일치). 버킷은 추세를 가시화합니다. 월요일마다 “할당량”이 급증한다면, 소방 대응 대신 계획을 세웁니다.
  • 비용 렌즈: 작업당 예상 비용을 로깅합니다. 입력, 출력, 재시도 포함하여 주간으로 집계합니다. 정확성이 목적이 아닙니다. 기울기를 느끼는 것이 목적입니다. 간단한 백분위수 뷰(P50, P95)는 몇 가지 이상값이 조용히 예산을 잡아먹고 있는지 보여줍니다.

알림에 대한 작은 메모: 지루하게 유지합니다. 요란하지 않게, 행동으로 이어지는 임계값만: “실패 버킷 > X, Y분 동안” 또는 “비용 P95가 전주 대비 > Z% 상승.” 오탐 속에 사는 것보다 늦게 알아차리는 게 낫습니다. 절약된 에너지는 다른 곳에서 빛을 발합니다.

보안 & 컴플라이언스 기본 (키, 사용자 콘텐츠 처리)

여기서는 특별한 게 없습니다. 그리고 그게 포인트입니다. 기본이 대부분의 일을 해줍니다.

  • 키: API 키를 코드 밖에 두고 일정 주기로 교체합니다. 환경별 키, 스코프가 있다면 최소 권한, 팀 간 공유 금지. API가 단기 토큰을 지원하면 사용합니다.
  • 사용자 콘텐츠: 원시 사용자 데이터를 로깅하지 않습니다. 해시, 크기, 참조를 로깅합니다. 디버깅을 위한 샘플이 필요하면, 명확한 보존 타이머와 함께 먼저 스크러빙하거나 편집합니다.
  • 데이터 처리: 모든 작업에 테넌트 또는 사용자 ID를 태깅하고, 로그와 스토리지까지 그 태그를 유지합니다. 평범하지만, 접근 확인이 구전으로 전해지는 것을 막아줍니다.
  • 스토리지: 결과는 서버 측 암호화와 엄격한 ACL이 있는 버킷이나 데이터베이스에 저장됩니다. 여기서는 영리함보다 감사 추적이 더 중요합니다.
  • 컴플라이언스 자세: 팀에서 SOC 2 또는 GDPR 안심이 필요하다면, 무엇이 어디로 가는지, 누가 볼 수 있는지, 얼마나 오래 보존되는지 정확히 적어둡니다. 암묵적인 약속은 없습니다. 의심스러울 때는 추측하는 대신 벤더의 보안 페이지와 데이터 처리 약관을 확인합니다.

제 테스트는 간단합니다: 개인정보 보호에 민감한 동료에게 손을 흔들지 않고 이 설정을 설명할 수 있는가? 그렇지 않다면, 충분히 단순화하지 않은 것입니다.

마지막으로

저는 속도를 찾으려 들어왔습니다. 얻은 것은 안정감이었습니다. Seedance 2.0 API는 단계를 없애주지 않았습니다. 예측 가능하게 만들어 줬습니다. 그것만으로도 작업이 더 가볍게 느껴졌습니다. 한 달에 걸쳐 비용이 어떻게 추세를 보이는지, 그리고 새로운 작업 유형 아래에서 내 버킷이 잘 유지되는지 계속 지켜보고 있습니다. 조용한 질문들이지만, 좋은 질문들입니다. 여러분도 그렇게 생각하시나요?


Seedance 2.0처럼 영화 같은 영상을 만들고 싶으신가요? WaveSpeed Cinematic Video Generator를 사용해 지금 바로 Seedance 2.0 수준의 영화 같은 영상을 만들어 보세요.