추상화를 유출하지 않고 비디오 모델 간 라우팅하기
요약
다양한 비디오 생성 모델을 통합할 때 발생하는 파라미터 불일치, 비동기 처리 방식, 실패 모드 문제를 해결하기 위한 추상화 전략을 다룹니다. 공통 코어와 타입화된 패스스루를 결합하여 모델 간 라우팅을 효율적으로 구현하는 방법을 제안합니다.
핵심 포인트
- 최소 공통 분모 방식 대신 공통 코어와 타입화된 패스스루 조합 권장
- 비동기 작업 상태(JobState)를 정규화하여 폴링, 웹훅 등 차이점 은닉
- 실패 모드를 분류(retryable, policy, quota)하여 효율적인 재시도 로직 구현
- 모델별 파라미터(지속 시간, 종횡비 등)의 정규화 필요성 강조
단일 비디오 생성 모델을 기반으로 구축하면, 해당 모델이 지원 중단(deprecated)될 때 통합 코드를 다시 작성해야 합니다. 여러 모델을 기반으로 구축하면 어댑터(adapter)를 작성하는 데 시간을 소비하게 됩니다. 여기에서 저는 두 번째 옵션을 선택하며 배운 점들을 공유합니다.
파라미터가 일치하지 않음
모든 제공업체(provider)는 동일한 개념에 대해 서로 다른 생각을 가지고 있습니다:
- 지속 시간 (Duration): 어떤 곳은 초(seconds)를 사용하고, 어떤 곳은 프레임 수(frame counts)를 사용하며, 어떤 곳은 고정된 열거형(enum)만 허용합니다.
- 종횡비 (Aspect ratio):
"16:9"vs{width, height}vs 명명된 프리셋(preset) - 시드 (Seeds): 지원됨, 무시됨, 또는 조용히 비결정론적(non-deterministic)으로 작동함
- 이미지 컨디셔닝 (Image conditioning): URL, base64, 또는 미리 업로드된 에셋 ID
최소 공통 분모(lowest-common-denominator) 인터페이스는 유혹적이지만 잘못된 방식입니다. 비용을 지불하고 얻는 기능들을 잃게 되기 때문입니다. 제가 선택하여 효과를 본 대안은 **공통 코어(common core)와 타입화된 패스스루(typed passthrough)**의 조합입니다.
type VideoRequest = {
prompt: string
durationSec: number // 정규화됨, 어댑터가 변환함
...
코어는 모든 제공업체가 지원하는 내용을 다룹니다. 탈출구(escape hatch)를 마련해 두면, 특정 제공업체 전용 기능을 사용하더라도 인터페이스를 재설계할 필요가 없습니다.
비동기(Async)가 실제 어려운 부분임
비디오 생성에는 몇 분이 소요되므로 모든 제공업체는 비동기(asynchronous) 방식이며, 각자 자신만의 비동기 방식을 만들어냈습니다:
- 작업 ID(job ID)를 이용한 폴링(Polling)
- 웹훅 콜백(Webhook callbacks)
- 서버 전송 이벤트(Server-sent events)
- 때때로 역행하기도 하는 큐 위치(queue position) 필드
파라미터를 정규화하는 것보다 이를 정규화하는 것이 더 중요합니다. 효과적이었던 방법은 어댑터가 단일 poll(jobId): Promise<JobState>를 노출하고, 내부적으로 제공업체가 무엇을 하든 숨기는 것이었습니다. JobState는 queued | running | done | failed로 구성된 작은 판별된 유니온(discriminated union)이며, 사용 가능한 경우 정규화된 progress를 포함하고 그렇지 않으면 undefined를 가집니다.
진행 상황을 조작하지 마세요. 거짓말을 하는 스피너(spinner)는 "여전히 작업 중"이라고 말하는 스피너보다 더 나쁩니다.
실패 모드(Failure modes) 또한 정규화가 필요함
제공업체마다 실패하는 방식이 다릅니다: 콘텐츠 정책 거부, 일시적인 5xx 오류, 할당량 소진, 조용한 잘림(silent truncation). 이러한 것들을 공통된 분류 체계(taxonomy)로 매핑하는 것이 재시도 로직(retry logic)을 가능하게 만드는 핵심입니다:
retryable— 백오프(backoff) 및 재시도policy— 재시도하지 않음, 사용자에게 알림quota— 재시도하지 않음, 제공자(provider)를 전환하거나 명확하게 오류를 발생시킴
이를 잘못 설정하면 정책 거부(policy rejection)를 5번이나 재시도하게 되어, 결코 성공할 수 없는 요청에 크레딧(credits)을 낭비하게 됩니다.
제공자 간의 크레딧 (Credits across providers)
가격 책정이 서로 다른 여러 백엔드(backends)에 대해 하나의 잔액(balance)을 노출하려면, 정규화 테이블(normalisation table)이 필요하며 이것이 근사치(approximation)라는 점을 솔직하게 명시해야 합니다. 저는 HyperFrames에서 이를 실제 운영 환경(production)에 적용하고 있습니다. 텍스트나 이미지를 입력하면 Kling, Veo, Wan 및 기타 모델들로 라우팅되며, 하나의 잔액으로 관리됩니다.
주의 사항 (Caveat)
이러한 추상화(abstraction)는 제공자가 대략 3개 이상일 때 가치가 있습니다. 그 미만일 경우에는 어댑터(adapters)를 구축하는 비용이 절감되는 비용보다 더 큽니다. 그럴 때는 그냥 통합(integrations) 코드를 직접 작성하고 넘어가십시오.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기