튜토리얼
API로 동영상에 텍스트 추가하기: 자막과 오버레이 워크플로
동영상 위의 텍스트가 단순한 프롬프트가 아니라 API 설계 문제인 이유
생성된 동영상에 텍스트를 추가하는 것은 프롬프트 한 줄을 고치는 것처럼 보입니다. 자막을 설명하고, 요청을 보내고, 텍스트가 들어간 클립을 받으면 됩니다. 하지만 실제로는 자막이 읽을 수 있어야 하고, 타이밍이 맞아야 하고, 현지화가 가능해야 하거나, 여러 장면에서 재사용되어야 하는 순간이 오면 프롬프트는 더 이상 흥미로운 부분이 아닙니다. 아키텍처가 핵심입니다.
서로 다른 코드베이스로 이어지는 두 가지 실행 가능한 경로가 있습니다:
- 생성 시 네이티브 텍스트. 동영상 모델이 텍스트를 장면의 일부로 렌더링하도록 합니다. 요청 한 번, 소재 하나, 후처리 없음.
- 깨끗한 소재 + 오버레이. 먼저 텍스트가 없는 소재를 생성하고(또는 텍스트 없는 구도를 요청하고), 두 번째 렌더링 단계에서 자막과 오버레이를 합성합니다.
왜 이것이 단순한 미적인 문제가 아닐까요? 다운스트림의 모든 것이 이 선택에 달려 있기 때문입니다. 텍스트가 생성된 픽셀 안에 존재하면 스타일을 쉽게 다시 디자인하거나, 번역하거나, 음성에 맞춰 타이밍을 정렬하거나, 클립을 다시 생성하지 않고 오타를 고칠 수 없습니다. 텍스트가 별도의 오버레이에 존재하면 레이아웃, 폰트, 타이밍을 직접 장악하지만, 이제 직접 렌더러를 보유해야 하고 파이프라인에 요청이 하나 더 늘어납니다.
이 가이드의 나머지 부분에서는 동영상 위의 텍스트를 실제 프로덕션 환경에서 흔히 그러하듯 처리합니다. 즉, 데이터 문제(어떤 텍스트가, 어디에, 언제)에 렌더링 문제(누가 그것을 그릴 것인가)를 더한 것으로 다룹니다. 이 둘을 분리하고 나면 API 호출은 단순해지고, 자막 로직을 다시 작성하지 않고도 생성 백엔드 사이를 전환할 수 있습니다.
유용한 사고 모델은 항상 세 가지를 구분하는 것입니다: 콘텐츠(텍스트 자체), 타이밍(언제 나타나는가), 표현(폰트, 위치, 스타일). 네이티브 생성은 이 셋을 모두 불투명한 한 단계로 압축합니다. 오버레이는 이 셋을 모두 명시적이고 편집 가능하게 유지합니다. 어느 쪽이 보편적으로 더 나은 것은 아니며, 목표는 통합을 작성하기 전에 어느 쪽에 약속할지 아는 것입니다.
경로 1: 프롬프트를 통한 생성 시 네이티브 화면 텍스트
네이티브 텍스트는 생성 모델이 문자를 동영상 프레임 안에 그리는 것을 의미합니다. 텍스트가 구워진 단일 출력 소재를 얻게 되고, 파이프라인은 짧게 유지됩니다: 프롬프트 구성, 제출, 폴링, 전달.
프롬프트에 무엇을 넣을까
모델이 렌더링하는 타이포그래피는 합성된 텍스트보다 예측 불가능하므로, 프롬프트를 전체 디자인 시스템을 설명하는 곳이 아니라 좁은 지시로 취급하세요. 실용적인 조언:
- 텍스트를 짧게 유지하세요. 장면당 몇 단어면 충분합니다. 긴 문장은 철자 흐트러짐과 레이아웃 혼잡을 유발하기 쉽습니다.
- 리터럴 문자열은 따옴표로 인용하세요. 텍스트가 정확히 나타나야 한다면 따옴표로 묶고 그 정확한 텍스트를 요구하세요. 모호한 설명은 모호한 결과를 낳습니다.
- 위치와 스타일을 명시하세요. "하단 3분의 1에 중앙 정렬된 큰 흰색 텍스트"는 실행 가능합니다. "예쁜 제목"은 그렇지 않습니다.
- 장면당 텍스트 요소 하나. 같은 클립에서 제목, 부제목, 워터마크를 요구하면 실패 모드가 배로 늘어납니다.
- 언어를 명시하세요. 문안이 영어가 아니라면 이를 밝히세요. 글리프 커버리지가 각기 다르기 때문입니다.
무엇이 반환될지 예상하기
출력을 명세가 아니라 초안으로 취급하세요. 좋은 결과조차도 글리프 변형, 불규칙한 자간, 움직임에 부분적으로 가려짐이 나타날 수 있습니다. 전달 전에 항상 렌더링된 프레임을 확인하세요. 저렴한 프로그래밍 방식 검사와 텍스트가 포함된 첫/마지막 프레임을 사람이 훑어보는 것만으로 대부분의 문제를 잡을 수 있습니다.
이 경로가 유리한 경우
텍스트가 부수적이고 분위기용일 때 네이티브 텍스트가 올바른 기본값입니다: 배경 거리의 간판, 제품 위의 스타일화된 로고, 잘못되면 다시 생성해도 되는 짧은 타이틀 카드 등입니다. 두 번째 렌더링 단계를 정말 감당할 수 없을 때도 유리합니다. 예를 들어 촉박한 납기 흐름에서 클립당 요청 한 번이 예산인 경우입니다.
대가는 통제력입니다. 문구, 스타일 또는 타이밍을 바꾸려면 새로운 생성 요청이 필요하고, 결과는 재시도 사이에 결정적이지 않습니다. 재생성된 클립이 이전 클립을 깔끔하게 대체할 수 있도록 프롬프트와 저장 구조를 설계하세요.
경로 2: 깨끗한 소재를 생성하고 텍스트를 직접 합성하기
오버레이 경로는 작업을 둘로 나눕니다. 먼저 의도적으로 텍스트 없는 소재를 생성합니다. 그다음 렌더링 서비스나 자체 미디어 파이프라인을 사용해 그 위에 자막과 오버레이를 합성합니다.
먼저 깨끗한 소재 생성
텍스트 없는 구도를 요청하세요: 장면, 피사체, 카메라 움직임, 조명을 설명하고 화면 텍스트 요구를 명확히 피하세요. 모델이 간판이나 UI 텍스트를 지어내는 경향이 있다면 화면에 읽을 수 있는 텍스트가 없어야 한다고 언급하세요. 여기의 출력은 기본 소재입니다. 즉, 여러 텍스트 변형에 걸쳐 재사용할 수 있는 안정적인 입력입니다.
후합성
오버레이 단계는 두 입력을 소비합니다: 기본 소재와 구조화된 자막/오버레이 페이로드. 렌더러를 직접 제어하므로 프롬프트가 보장할 수 없는 속성을 얻습니다:
- 확정된 폰트와 크기. 같은 페이로드는 매번 동일하게 렌더링되며, 이는 브랜드 일관성에 중요합니다.
- 정밀한 타이밍. 자막은 음성, 음악 비트 또는 장면 전환에 프레임 단위로 정렬될 수 있습니다.
- 안전 영역과 레이아웃 규칙. 여백을 강제하고, 얼굴과 주요 피사체를 피하며, 텍스트를 플랫폼별 안전 영역 안에 유지할 수 있습니다.
- 저렴한 반복. 오타를 고치거나 자막을 번역하는 것은 오버레이를 다시 렌더링하는 일이지 동영상을 다시 생성하는 일이 아닙니다.
- 조합 가능성. 워터마크, 하단 3분의 1 띠, 자막을 독립적으로 레이어링하고 출력별로 전환할 수 있습니다.
트레이드오프
오버레이 경로는 모델 의존성을 파이프라인 복잡성과 맞바꿉니다. 요청이 하나 늘고, 중간 소재를 저장할 곳이 필요하며, 렌더링 구성을 직접 소유하게 됩니다. 그 대가로 텍스트는 버전 관리하고 테스트하고 교체할 수 있는 데이터가 됩니다. 실제 자막 요구가 있는 모든 것—접근성 자막, 다국어 변형, 템플릿화된 소셜 포맷—에는 이 거래가 거의 항상 가치가 있습니다.
실용적인 패턴은 오버레이를 비파괴적으로 렌더링하는 것입니다: 기본 소재와 자막 페이로드를 단일 진실 공급원으로 두고, 합성된 동영상은 언제든 다시 생성할 수 있는 파생 산출물로 취급하세요.
두 경로 모두에서 살아남는 자막 페이로드 설계
자막 데이터를 잘 설계하면 네이티브 생성과 오버레이 사이의 선택은 재작성이 아니라 렌더링 세부 사항이 됩니다. 목표는 네이티브 텍스트용으로 프롬프트로 직렬화되고 오버레이용으로 렌더러에 공급될 수 있는 단일 페이로드입니다.
핵심 필드
각 텍스트 요소를 콘텐츠, 타이밍, 표현이 분리된 객체로 모델링하세요:
{
"track": "captions",
"items": [
{
"text": "Everything you need to know",
"start": 0.0,
"end": 2.4,
"style": { "role": "title", "align": "center", "position": "lower_third" },
"lang": "en"
}
]
}
핵심 설계 결정:
- 시간은 초 단위의 부동소수점. 프로젝트 전체에서 단일 시간 기준을 사용하고, 프레임 변환은 렌더링 시에만 수행하세요.
- 절대값이 아닌 역할로 스타일 지정.
title,caption,lower_third같은 역할을 이름 붙이고 렌더러에서 이를 폰트와 크기로 해석하세요. 이렇게 하면 페이로드가 포맷 간에 이식 가능해집니다. - 트랙 이름.
captions,titles,watermark트랙을 분리하여 출력별로 트랙을 활성화하거나 비활성화하세요. - 항목별 언어 태그. 폰트 선택과 이후 번역 채널 모두에 필요합니다.
- 텍스트 필드에 표현 정보를 굽지 마세요. 스타일 힌트를 절대 텍스트에 넣지 마세요. 그것이 바로 스타일 객체의 용도입니다.
하나의 스키마에서 두 경로로 공급하기
네이티브 생성의 경우, 가장 중요한 항목을 선택해 프롬프트 지시로 변환하는 작은 어댑터를 작성하세요: 리터럴 텍스트, 위치, 스타일 설명자. 이 어댑터를 의도적으로 손실성으로 만드세요. 네이티브 렌더링은 어차피 전체 자막 트랙을 따를 수 없습니다.
오버레이의 경우, 페이로드를 렌더러에 전달하면 렌더러가 역할과 위치를 해석하고 각 항목의 시간 창 동안 그것을 그립니다.
보상은 다음과 같습니다: 애플리케이션 코드가 자막 객체를 하나 구성하고, 네이티브에서 오버레이로(또는 특정 클립을 위해 다시 전환) 전환하는 것은 데이터 마이그레이션이 아니라 구성 변경입니다.
실제로 작동하는 오버레이 요청, 단계별 설명
다음은 API 형태의 단계로 표현한 오버레이 경로의 시퀀스입니다. 정확한 엔드포인트 이름과 매개변수는 제공자에 따라 다르지만 형태는 안정적이며, 얇은 추상화 계층이 이를 백엔드 전반에 걸쳐 안정적으로 유지합니다.
1단계: 깨끗한 소재 생성 제출
POST /video/generations
{
"prompt": "A slow dolly shot through a modern office, no on-screen text, natural light",
"duration_seconds": 6,
"aspect_ratio": "16:9"
}
프롬프트를 텍스트 없이 유지하고 반환된 작업 식별자를 저장하세요.
2단계: 소재가 준비될 때까지 폴링
GET /video/generations/{job_id}
빡빡한 루프가 아닌 백오프로 폴링하세요. queued, running, succeeded, failed 같은 상태를 코드의 일급 상태로 취급하고 결과 소재 URL 또는 ID를 영속화하여 충돌로 생성 결과를 잃지 않게 하세요.
3단계: 오버레이 렌더링 제출
POST /video/overlays
{
"source_asset": "asset_abc123",
"captions": {
"track": "captions",
"items": [
{ "text": "Shipping today", "start": 0.4, "end": 2.0, "style": { "role": "title", "position": "lower_third", "align": "center" }, "lang": "en" },
{ "text": "No rebuild required", "start": 2.2, "end": 4.0, "style": { "role": "title", "position": "lower_third", "align": "center" }, "lang": "en" }
]
}
}
오버레이 요청은 소재와 텍스트 데이터만 전달합니다. 소재를 다시 생성하지 않으며, 바로 이것이 반복을 저렴하게 만듭니다.
4단계: 합성 결과 가져오기
생성 작업을 폴링한 것과 같은 방식으로 오버레이 작업을 폴링한 다음 최종 소재를 가져오세요. 기본 소재, 자막 페이로드, 출력을 함께 저장하여 합성을 재현 가능하게 하세요.
코드에서는 1~4단계를 프롬프트와 자막 페이로드를 받아 최종 소재를 반환하는 함수로 감싸세요. 그 함수가 진정한 인터페이스이고, 개별 엔드포인트는 교체 가능한 구현 세부 사항입니다.
출시 전 신뢰성 점검
동영상 텍스트 파이프라인은 조용한 방식으로 실패합니다: 잘린 자막, 잘못 번역된 줄, 정리되지 않은 재생성. 사용자가 보기 전에 점검을 추가하세요.
렌더링된 텍스트 검증
- 네이티브 경로: 각 텍스트 창의 시작과 끝에서 프레임을 샘플링하고 확인하세요. 기본적인 가독성 검사는 가능한 한 자동화하되, 고객 대상 문안은 사람 검토 단계를 유지하세요. 모델 타이포그래피가 예측 불가능하게 퇴화할 수 있기 때문입니다.
- 오버레이 경로: 렌더링 전에 페이로드를 검증하세요—각 항목의 타이밍이 겹치지 않고, 클립 길이 내에 있으며, 지원되는 언어와 역할을 사용하는지 확인하세요. 렌더링 후에는 항목 경계에서 프레임을 표본 확인하여 아무것도 잘리지 않았는지 확인하세요.
재시도를 안전하게 만들기
- 제출 요청의 멱등성 키를 사용하여 재시도 네트워크 호출이 중복 생성 작업을 만들지 않게 하세요.
- 작업별 상태 머신: queued → running → succeeded/failed, 영속화하여 재시작이 처음부터 다시 시작하는 대신 복구되게 하세요.
- 제한된 폴링을 백오프와 타임아웃과 함께 사용하여 영구 루프 대신 명확한 실패를 제시하세요.
- 중간 소재 저장. 오버레이 단계가 실패하면 이미 보유한 소재를 다시 생성하는 데 돈을 써서는 안 됩니다.
전체 체인의 비용 관리
오버레이 경로에는 생성과 렌더링이라는 두 개의 과금 단계가 있으므로 둘 다 모니터링하세요. 소재를 다시 생성하는 빈도와 오버레이만 다시 렌더링하는 빈도를 추적하세요. 소재 재생성이 지배적이라면 해당 워크로드에 대한 네이티브/오버레이 구분이 잘못되었을 수 있습니다. (기본 소재, 자막 페이로드) 단위로 최종 소재를 캐시하여 같은 출력에 대한 반복 요청이 무료가 되게 하세요.
관측 가능성
프롬프트, 자막 페이로드 해시, 작업 ID, 최종 소재 ID를 함께 기록하세요. 사용자가 자막 오류를 보고하면 문제가 생성, 데이터 또는 렌더링 중 어디에 있는지 몇 초 만에 파악할 수 있기를 원합니다. 자막 페이로드를 버전 관리된 단일 진실 공급원으로 두어 렌더러가 무엇을 그리도록 요구받았는지 항상 설명할 수 있게 하세요.
기본 워크플로 선택하기
이를 시작 규칙으로 삼고 제품이 성장함에 따라 재검토하세요.
네이티브 텍스트를 선택하세요 텍스트가 장식적이거나 부수적이고, 문구가 짧으며, 오류 수정 방식으로 재생성을 받아들일 수 있고, 두 번째 렌더링 단계를 추가하는 것이 해당 클립에 엔지니어링 시간을 들일 가치가 없을 때.
오버레이 경로를 선택하세요 텍스트가 정확해야 하고, 타이밍이 있어야 하고, 번역되거나 스타일이 다시 디자인되거나 여러 출력에 걸쳐 재사용되어야 할 때. 자막, 부제목, 하단 3분의 1 띠, 템플릿화된 마케팅 포맷은 거의 항상 여기에 해당합니다. 추가 요청은 결정성의 대가입니다.
혼합은 합리적입니다. 많은 팀이 기본적으로 깨끗한 소재를 생성하고, 중요한 모든 것에는 오버레이에 의존하며, 네이티브 텍스트는 일회성 분위기 장면에 남겨둡니다. 두 경로가 동일한 자막 페이로드를 소비하므로 데이터를 특별 취급할 필요 없이 각 클립에 대해 이 결정을 내릴 수 있습니다.
어느 쪽을 선택하든 통합을 얇은 추상화 뒤에 유지하세요: 프롬프트와 자막 페이로드를 받아 최종 소재를 반환하는 함수. 그 경계가 바로 생성 백엔드를 전환하거나, 렌더링 옵션을 추가하거나, 공들여 제대로 만든 자막 로직을 건드리지 않고 개별 클립을 워크플로 사이에서 이동할 수 있게 하는 것입니다.
관련 글
모든 아티클 보기 →-
튜토리얼
HiAPI API로 hailuo-2.3-fast/image-to-video 호출하는 방법: curl과 Python 실전
빠르고 저렴한 이미지-투-비디오 등급의 첫 실행 요청 — 모델 ID부터 MP4 다운로드까지
-
튜토리얼
HiAPI API로 hailuo-2.3/text-to-video를 호출하는 방법: curl, Python 및 실행 가능한 요청 예시
-
튜토리얼
HiAPI에서 Kandinsky 6.0 Pro 텍스트-투-비디오 사용하기: curl 및 Python 튜토리얼
요청 우선 실전 데모: 프롬프트에서 비디오 클립까지의 호출 구성, 요청 제출, 완료 상태 폴링 및 결과 다운로드