쇼츠 영상 하나를 만드는 일은 생각보다 손이 많이 갑니다. 스크립트를 쓰고, 녹음하고, 이미지를 구하고, 자막을 넣고, 편집까지. "제목만 입력하면 나머지는 AI가 알아서"라는 목표로, 스크립트 생성부터 MP4 렌더링까지 전 과정을 자동화한 파이프라인을 만들었습니다.
쇼츠 영상은 짧지만 만드는 과정은 만만치 않습니다. 아이디어를 스크립트로 만들고, 읽어주는 목소리를 녹음하고, 장면에 맞는 이미지를 구하고, 자막을 넣고, 편집해서 내보내야 합니다. 이 반복 작업을 없애고 싶었습니다. 그래서 제목과 키워드 하나만 넣으면 완성된 쇼츠 영상이 나오는 파이프라인을 만들기로 했습니다.
시스템은 크게 둘로 나뉩니다. 사용자가 작업을 만들고 진행 상황을 보는 Next.js 프론트엔드와, 스크립트 분석부터 영상 렌더링까지 담당하는 FastAPI 백엔드입니다. 백엔드는 무거운 AI 작업(텍스트 생성, 이미지 생성, TTS, FFmpeg 인코딩)을 순차적으로 처리하는 파이프라인 구조로 설계했습니다.
| 단계 | 상태 | 작업 내용 |
|---|---|---|
| 1 | parsing | 스크립트 분석 → 장면(씬) 분할 |
| 2 | generating_images | 씬별 이미지 생성 |
| 3 | generating_audio | TTS 나레이션 오디오 생성 |
| 4 | awaiting_render | 이미지 검수 (사용자 확인) |
| 5 | rendering | FFmpeg 렌더링 (이미지+음성+자막) |
| 6 | done | 완료 — 미리보기 제공 |
각 작업은 pending부터 done까지 상태 머신으로 관리되며, 어느 단계에서 실패하든 error 상태로 빠져 사용자가 원인을 확인할 수 있습니다.
첫 관문은 AI가 쓴 스크립트를 영상의 장면(씬) 단위로 쪼개는 일입니다. 각 씬은 나레이션 문장, 해당 장면을 그릴 이미지 프롬프트, 예상 재생 시간(duration)을 갖습니다. 이 데이터가 이후 이미지 생성과 자막 생성의 기반이 됩니다.
class Scene:
narration: str # 나레이션 문장
image_prompt: str # 이미지 생성용 프롬프트
duration_estimate: float # 예상 재생 시간(초)
subtitle_chunks: list[str] # 자막 청크
스크립트의 문장 흐름을 끊어 각각의 장면으로 배정하고, 문장 길이에 비례해 재생 시간을 추정했습니다. 이후 자막은 문장을 다시 짧은 청크 단위로 잘라 화면에 자연스럽게 표시되도록 했습니다.
쇼츠에서 목소리는 절반 이상의 몫을 합니다. 그래서 톤이 뚜렷하게 다른 6가지 목소리를 준비했습니다. 여성은 차분한 Alloy, 따뜻한 Nova, 밝은 Shimmer, 남성은 또렷한 Echo, 깊은 Onyx, 서정적인 Fable로, 콘텐츠 성격에 맞게 고를 수 있습니다.
이미지는 3가지 스타일을 지원합니다. 사실적(realistic) — 실제 사진처럼 디테일하게, 애니(animation) — 일러스트·만화 스타일, 미니멀(minimal) — 깔끔하고 단순하게. 씬마다 이미지 프롬프트를 생성하므로 전체 영상이 일관된 스타일을 유지합니다.
이미지 생성이 끝나면 awaiting_render 상태로 멈춥니다. AI가 만든 이미지가 기대와 다를 수 있으므로, 사용자가 장면별 이미지를 확인하고 마음에 안 드는 장면만 다시 생성할 수 있게 한 것입니다. 이 단계 덕분에 완성 영상의 품질을 사용자가 직접 통제할 수 있습니다.
검수가 끝나면 렌더링을 시작합니다. 이때 재생 시간, 이미지 크기, 자막 정렬(좌/중/우) 옵션을 조정할 수 있습니다.
마지막 단계는 FFmpeg로 이미지·음성·자막을 합성해 MP4를 만드는 일입니다. 렌더링은 수십 초에서 수 분까지 걸릴 수 있기 때문에, SSE(Server-Sent Events)로 서버의 진행률을 실시간 스트리밍해 사용자에게 현재 단계와 퍼센트를 바로 보여줍니다.
// 프론트엔드에서 SSE로 진행률 수신
const es = new EventSource(`/api/status/${jobId}`)
es.onmessage = (e) => {
const { status, progress, message } = JSON.parse(e.data)
updateUI(status, progress, message)
}
쇼츠를 "만들기"가 아니라 "지시하기"로 바꾼 것이 이 프로젝트의 목표였고, 파이프라인이 그 역할을 해내고 있습니다. 지금도 실제 쇼츠 제작에 사용 중입니다.