한 줄의 아이디어를 40초 코미디 영상으로 만드는 10-beat 파이프라인
요약
Gemma 4 31B, HiDream, LTX-2, Irodori-TTS 등 다양한 AI 모델을 연결하여 한 줄의 아이디어로부터 40초 분량의 코미디 영상을 30분 내에 자동 생성하는 파이프라인을 소개합니다. 로컬 GPU 환경에서 API 비용 없이 전체 공정을 자동화하여 기존 수동 편집 방식 대비 제작 시간을 획기적으로 단축했습니다.
핵심 포인트
- Gemma 4 31B를 활용한 10-beat 구조의 스토리보드 및 계획 수립 자동화
- 이미지 생성(HiDream), 비디오 생성(LTX-2), 음성 생성(Irodori-TTS) 모델의 유기적 결합
- 로컬 GPU(Blackwell 96GB) 기반의 비용 효율적인 워크플로우 구축
- 수동 편집 대비 제작 시간을 며칠에서 30분 이내로 단축하는 파이프라인 설계
TL;DR
한 줄의 아이디어를 Gemma 4 31B가 10-beat 구조로 전개하고, HiDream으로 2048² 이미지 11장, LTX-2 A2V/I2V로 11개 클립(clip), Irodori-TTS로 대사와 남성 나레이션, ffmpeg로 자막 삽입 및 Hook 타이틀 오버레이(overlay)까지 전 과정을 자동화합니다. 실측 25~30분 만에 40초 분량의 세로형 (512×768) 영상을 생성합니다. 모든 과정은 로컬 GPU 1장 (96 GB Blackwell)으로 수행되며, API 비용은 0원입니다.
완성본 (공개됨):
이 글의 대상 독자
로컬 GPU를 사용하여 AI 영상 코미디를 양산하고 싶은 개인 개발자. 각 모델 단독 사용이 아닌 **「여러 모델을 하나의 파이프라인 (pipeline)으로 연결하여 운용하는 설계」**에 관심이 있는 사람.
수행 내용
Z세대 대상의 다크 코미디 풍자(「No Means Yes?」라는 성적 동의 딜레마를 소재로 하는 해외 쇼츠 영상 포맷)를, 한 줄의 아이디어에서 40초 영상 제작까지 자동화했습니다.
완성 이미지:
Hook (0-5s): 아름다운 여성의 클로즈업 + 나레이터 「'남자애잖아'라는 말에 응답한 그의 운명——」 + 대형 타이틀 「No Means Yes?」 -
본편 (5-37s): 영화관 데이트 → 「키스해도 돼?」 → 「싫어… 안 돼…」 → 낙담 → 「좀 더 적극적으로 해봐. 남자애잖아?」 → 그렇구나 → 키스 -
결말 (37-40s): 법원 「주문 피고인을 부동의 성교 등의 죄로 징역 3년에 처한다」 + 의사봉 「쾅!」 + 감옥에서 눈물 -
before / after:
| 기존 방식 | 이 파이프라인 (pipeline) |
|---|---|
| 아이디어 → 공개 영상 | 2~3일 (수동 편집) |
| API 비용 | DALL-E + 영상 생성으로 1편당 수백 엔 |
| 자막 | 수동으로 SRT 작성 |
| Hook | 별도 촬영 |
아키텍처 (Architecture)
[Stage A] Gemma 4 31B (vllm, port 8894) → plan.json (10 beats + hook)
[Stage B] HiDream-O1-Image (port 8895) → 11장의 2048² 이미지
+ Gemma 4 31B multimodal로 시각적 판단 (judge) (--judge --max-retries 2)
...
구현은 llm_server/storyboard/ 하위에 집약되어 있습니다 (pipeline.py / visual.py / judge.py / video.py / render.py / run.py).
10-beat consent_dilemma 포맷
prompts.py의 CONSENT_DILEMMA_SYSTEM에서 시스템 프롬프트 (system prompt)로 고정:
| # | type | speaker | renderer | 내용 |
|---|---|---|---|---|
| 1 | provocation | b | LTX-2 A2V | 의미심장한 유혹 |
| ... | a (silent) | LTX-2 I2V | 남자의 키스 순간 | |
| 8 | verdict | judge | LTX-2 A2V | 빠른 판결 |
| 9 | gavel_se | judge | LTX-2 I2V (keep_audio) | 의사봉 + AI 자동 「쾅!」 소리 |
| 10 | jail | a (silent) | LTX-2 I2V | 감옥에서 눈물 |
핵심 포인트는 3단계의 반전입니다:
거절 (refusal)을 단순한 「안 돼」로 만들지 않음: 「싫어… 안 돼…」와 같이 어미를 늘려, 연극적인 「No that doesn't mean No」의 뉘앙스를 줍니다. 이것이 이후 가스라이팅 (gaslight)의 모순을 성립시키는 전제가 됩니다. -
가스라이팅 직후에 키스를 배치하지 않음: 「pause」(그렇구나)를 1.5초간 삽입합니다. 템포와 감정 곡선을 위해서입니다. -
판결 (verdict)과 감옥 (jail)의 2단 결말: 판결만 있으면 갑작스럽습니다. 감옥에서 우는 장면이 있으면 「그는 정말로 유죄가 되었다」는 사실이 납득됩니다. -
Hook 설계 (TikTok의 초기 3초 문제)
세로형 숏폼은 처음 3초에서 이탈률이 결정된다. 본편 10 beats 앞에 Hook segment를 전치:
"hook": {
"title_overlay": "No Means Yes?",
"narrator_line": "「남자애잖아」라는 말에 응답한, 그의 운명——",
...
구현상의 함정 2가지:
함정 1: narrator TTS 길이가 duration_sec를 초과하면 음성이 끊김. 「그의 운명」의 「그」 부분에서 audio cut-off가 발생했다. 대책: TTS를 먼저 생성 → ffprobe로 실측 → max(plan_duration, narrator + 0.6)을 I2V duration에 전달.
narrator_dur = _ffprobe_duration(narrator_wav)
duration = max(float(hook.get("duration_sec", 0.0)), narrator_dur + 0.6)
ltx_i2v_clip(portrait, i2v_prompt, duration, silent_video, keep_audio=False)
함정 2: drawtext의 y 위치. y=h*0.30 (화면 1/3 높이)로 설정하면 얼굴을 가린다. y=20 (절대값 20 px)로 최상단에 배치.
자막 삽입 (무음 시청 대응)
전철에서 소리를 끄고 보는 사용자 대응 + 플랫폼 간 신뢰성을 위해, burned-in subtitles(자막 삽입)를 적용한다.
style = (
"FontName=Noto Sans CJK JP,FontSize=18,PrimaryColour=&H00FFFFFF,"
"OutlineColour=&H00000000,Outline=2,Shadow=0,BorderStyle=1,"
...
Alignment=2 = bottom center. MarginV=60으로 최하단에서 여유를 둠.
장문 분할: 하나의 beat 내에 30자 이상의 line이 있으면 얼굴을 가린다. _split_subtitle로 문장 부호 。.!?에서 문장을 분할 → 탐욕 알고리즘(Greedy algorithm)을 사용하여 28자 이하의 chunk로 생성 → beat duration을 균등하게 배분:
입력:
말투로 확인하는 건 로맨틱하지 않잖아. 있지, 좀 더 적극적으로 해봐. 남자애잖아?
출력 (하나의 8.9s beat를 2개의 chunk로 시간 분할):
| 시간 | 자막 |
|---|---|
| 15.16-19.63s | 말투로 확인하는 건 로맨틱하지 않잖아. |
| 19.63-24.10s | 있지, 좀 더 적극적으로 해봐. 남자애잖아? |
LTX-2 I2V로 효과음 생성하기 (gavel_se)
LTX-2 distilled는 I2V 출력 mp4에 AI 생성 음성 (환경음 / 효과음)을 자동으로 포함한다. ffmpeg -map 0:v:0 -map 1:a:0으로 명시적으로 drop 하지 않는 한, prompt로부터 소리가 따라온다.
이를 SE(효과음) 생성기로 전용한다:
def render_se_tail_beat(sb_dir, beat, prior_clip, work_dir):
# 1. 이전 beat의 최종 프레임 추출
extract_last_frame(prior_clip, last_frame_png)
...
ltx_i2v_clip에 keep_audio=True 플래그를 추가하여, ffmpeg 재인코딩 시 음성을 drop 하지 않는 경로를 만들었다.
gavel_se용 prompt:
"Single decisive arm motion of the judge bringing the gavel down sharply "
"onto the wooden bench. Loud sharp wood-on-wood thwack impact sound. "
"Brief, contained, no other motion in the frame."
판사의 최종 프레임 + 망치 prompt를 통해 「쾅!」 하는 소리가 나온다. 만약 실패할 경우 역전재판 SE 등으로 fallback(대체)하는 설계다.
함정 리스트
개발 중에 겪은 주요 함정 5가지:
1. vLLM 0.20.2에서 Codex CLI가 멈춤 (hang)
codex exec -p gemma4 명령어로 system prompt + idea를 던지면, /v1/responses의 handshake 단계에서 CPU 사용률 0%인 상태로 20분 이상 멈춤(hang) 현상이 발생한다. subprocess 출력을 tail -200으로 파이프(pipe) 처리한 탓에 초기 stderr(표준 에러) 정보도 차단되었다.
회피 방법: codex를 통한 호출은 포기하고 urllib.request를 사용하여 /v1/chat/completions를 직접 호출한다. response_format={"type":"json_object"}를 사용하여 JSON 형식을 강제했다. 그 결과 25초 만에 plan.json 생성을 완료할 수 있었다.
2. HiDream이 cinema screen을 제거하지 못함
setting_prompt에 "The movie screen is BEHIND the camera and NOT VISIBLE in frame"(영화 스크린은 카메라 뒤에 있으며 프레임 내에 보이지 않음)라고 명시해도, 2048/50 steps 설정에서도 스크린이 배경에 계속 남는다.
회피 방법: t2i(text-to-image)로 scene_base를 생성한 뒤, 동일한 이미지를 I2I(image-to-image) edit에 투입하여 "스크린을 dark wall(어두운 벽)로 교체하고 캐릭터 위치는 동일하게 유지"하라는 프롬프트를 입력하면 한 번에 제거된다. 저해상도 생성 → I2I fix → 해상도를 높여 모든 beat 재생성 순의 2단계 방식을 사용한다.
3. HiDream이 lips-on-lips 키스를 cheek kiss로 처리함
standard amplifier 환경에서 HiDream은 키스를 cheek kiss(볼 키스)로 해석하는 경향이 있다. "CRITICAL: their LIPS meet directly — mouth-to-mouth contact at the CENTER of the frame. NOT a cheek kiss"(중요: 입술이 직접 맞닿아야 함 — 프레임 중앙에서 입과 입이 접촉해야 함. 볼 키스가 아님) 수준의 강력한 지시(directive)가 필수적이다. _beat_edit_prompt에 kiss beat 전용 조기 반환(early return) 블록을 마련했다.
4. CAST / CROP_BOX / SPEAKER_A2V_PROMPT가 2인으로 하드코딩됨
a(Kenta)와 b(Misaki)만 알고 있는 CAST dict / CROP_BOX dict / SPEAKER_A2V_PROMPT dict가 3곳에 존재한다. judge나 narrator를 추가하려면 3개의 dict를 동시에 업데이트해야 한다(KeyError를 보고 나서야 인지하게 된다). setting_override를 가진 beat는 scene_base가 아닌 beat 자체의 이미지에서 portrait crop을 수행하도록 render_speech_beat_ltx_a2v에 분기 로직을 추가했다.
5. Gemma 4 multimodal judge의 과도한 false-positive (오탐)
storyboard/judge.py에서 Gemma 4 31B에 beat 이미지와 기대 표정을 전달하여 YES/NO를 판정하는 시각 judge(visual judge)를 구현했다. 확실히 "손가락 개수 이상", "입을 벌린 대화 포즈(open-mouth conversational pose)", "장면 기하학적 불일치(scene geometry mismatch)"와 같은 명백한 실패는 잡아내지만, "미묘하게 수줍은 표정(subtle shy expression)"과 같은 미세한 영역에서는 FAIL을 연발한다.
실무 적용: max-retries를 2로 설정하여 3회 연속 FAIL이 발생하면 수용하고 진행한다. frontier reviewer (Gemini 3.1 Pro)로 전환하는 임계값(threshold)은 아직 자동화하지 않았다.
VRAM 공존 설계
96 GB Blackwell Max-Q에서의 내역:
| 프로세스 | idle (GiB) | peak (GiB) |
|---|---|---|
| Gemma 4 31B (NVFP4) | 38 | 38 |
| ... |
모두 peak 상태일 때 109 GiB가 되어 OOM(Out of Memory)이 발생한다. 운영 흐름은 다음과 같다:
- Stage A: Gemma 31B + HiDream idle 상태에서 peak ~62 GiB
- Stage B with judge: Gemma 31B + HiDream peak 상태에서 ~73 GiB
- 최종 정리 전 $
ightarrow$ 38 GiB 해제:pkill -f "vllm.*gemma"
Gemma kill -
Stage B 정리 (2048/50): HiDream peak ~33 GiB -
Stage C 전 $\rightarrow$ 16 GiB 해제 lsof -ti tcp:8895 | xargs kill
HiDream kill -
Stage C: LTX-2 + TTS + Ditto로 peak ~32 GiB
Stage 전환 시 명시적으로 kill 하는 것만으로 전 공정을 한 장의 메모리에 올릴 수 있다.
이터레이션 루프 (cache 전략)
"전부 다시 만들기"가 아니라 **부분 재생성 (Partial Regeneration)**이 고속화의 핵심:
# 1 beat만 이미지 regen (HiDream만 수행)
python -m storyboard.visual --plan ... --out ... --only-beat 7 --steps 50 --resolution 2048
# 부분 video regen (TTS + LTX-2)
...
캐시 계층 (Cache Hierarchy):
- HiDream beat 이미지 (
beat_NN_<type>.png) —--only-beat로 개별 80초 소요 - A2V / I2V clip (
clip_NN_*.mp4) — beat type / speaker / line 변경 시 invalid - Hook 완성본 (
clip_00_hook.mp4) — 타이틀 위치만 바꾸고 싶을 때는 이것만 삭제 (LTX-2 I2V의 무거운hook_silent.mp4는 재사용) - 자막 SRT — 매번 재생성 (10초)
타이틀 위치 / 자막 style / Hook 내 문구 조정은 30초 만에 다시 구울 수 있다. LTX-2 I2V의 100초 부분은 그대로 사용할 수 있다.
Kotonia에서 어떻게 활용되는가
이 파이프라인으로 생성한 영상은 SNS 배포 (TikTok / YouTube Shorts / IG Reels)용이며, Kotonia (kotonia.ai)의 어텐션(Attention) 획득 $\rightarrow$ 유료 결제 유도로 이어지는 상류(Upstream)를 담당한다.
기술적으로는 /studio/ (HiDream의 이미지 생성)와 동일한 스택을 영상 방향으로 확장한 것이다. 향후에는 /video-studio/로서 Web UI에서 클릭 한 번으로 동일한 파이프라인을 호출할 수 있는 구성을 상정하고 있다 (현재는 CLI로만 가능).
관련 기사 / 시도해보고 싶은 분들을 위해
- HiDream-O1-Image의 5개 기능을 1 GPU에 상주시키는 구성 — Studio (
/studio/)의 백엔드 설계 - LTX-2를 95GB GPU 1장에 담는 fp8-cast 양자화(Quantization) — Stage C의 영상 생성 기반
- 언어 학습 숏폼 영상을 Claude Code로 재현해 보았다 — 6-beat "망고 사건" 포맷의 선행 구현
- 시도해보고 싶은 분은
/studio/에서 이미지 생성 측면을 클릭 한 번으로 테스트할 수 있다 (영상화 CLI는 현재 self-host로만 제공).
AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기