본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 28. 17:47

2인 호스트 AI 대화 사양: A/B 화자 JSON을 사용하여 YouTube 롱폼 스크립트를 구성하는 방법

요약

YouTube 롱폼 영상을 제작하기 위해 A/B 화자 태그가 포함된 JSON 사양을 사용하여 자동화된 비디오 파이프라인을 구축하는 방법을 설명합니다. edge-tts와 Python을 활용해 두 명의 신경망 음성이 교차하는 대화형 영상을 효율적으로 렌더링합니다.

핵심 포인트

  • JSON 기반의 2인 호스트 대화 사양 설계
  • edge-tts를 활용한 신경망 음성(Neural voices) 합성
  • 슬라이드 변경 시점에 맞춘 자동화된 비디오 렌더링
  • ffmpeg와 Python을 이용한 오디오-비디오 결합 및 CI 통합

이 모노레포(monorepo)를 기반으로 운영되는 두 개의 YouTube 채널을 위해 제가 구축해 온 비디오 파이프라인은 단일 내레이터와 단일 슬라이드로 끝나는 짧은 세로형 클립에서 시작되었습니다. 롱폼(Longform)은 다릅니다. 내용이 좋더라도 하나의 목소리만 나오고 대화의 변화가 없는 10분짜리 설명 영상은 시청하기 어렵습니다. 저는 텍스트 음성 변환(Text-to-Speech, TTS) 오디오북이 아니라, 두 사람이 문제를 함께 논의하는 듯한 느낌을 주는 무언가를 원했습니다.

해결책은 2인 호스트 대화 사양(Two-host dialogue spec)이었습니다. 이는 오디오의 각 줄에 화자(A 또는 B)가 태그된 JSON 파일이며, 빌드 스크립트가 이를 두 개의 신경망 음성(Neural voices)이 교차하는 전체 길이의 영상으로 렌더링합니다.

사양의 모습

가장 단순한 형태의 사양은 다음과 같습니다:

{
  "title": "How Turso libSQL compares to Cloudflare D1",
  "description": "...",
...

몇 가지 주목할 점이 있습니다. slide 필드는 선택 사항입니다. 이 필드가 생략되면 빌드 스크립트는 오디오가 재생되는 동안 이전 슬라이드를 유지합니다. 이는 모든 문장마다 새로운 시각 자료를 만들 필요가 없음을 의미하며, 이는 유지 관리가 매우 힘들고 영상이 뚝뚝 끊기게 만들 것입니다. 새로운 슬라이드는 보여줄 가치가 있는 내용이 있을 때만 나타납니다.

speaker 필드는 특정 목소리에 매핑됩니다. build_longform.py에서는 다음과 같습니다:

VOICE = {
    "A": os.environ.get("LF_VOICE_A", "en-US-GuyNeural"),
    "B": os.environ.get("LF_VOICE_B", "en-US-AvaNeural"),
...

두 목소리 모두 edge-tts 신경망 음성입니다. 이는 Microsoft Edge의 텍스트 음성 변환(Text-to-speech) API를 사용하는 Python 래퍼(Wrapper)로, Azure 구독 없이도 브라우저와 동일한 신경망 음성에 접근할 수 있게 해줍니다. A/B 할당은 테스트를 통해 결정되었습니다. 설명(Exposition)을 위해서는 낮고 차분한 목소리를, 후속 질문이나 반론을 위해서는 더 대화체처럼 들리는 목소리를 사용합니다. 기본 음성을 특정 edge-tts 카탈로그 버전에서 사용할 수 없는 경우를 대비해 환경 변수로 두 가지 모두 재정의할 수 있습니다.

빌드 작동 방식

build_longform.py는 사양을 선형적으로 처리합니다:

  1. slide가 포함된 각 세그먼트에 대해, slides.py를 통해 슬라이드를 PNG로 렌더링합니다.
  2. 지정된 화자의 목소리로 edge-tts를 사용하여 세그먼트의 text를 합성하고, 이를 mp3로 저장합니다.
  3. PNG로부터 무음 비디오 클립을 생성한 다음, 이를 오디오와 mux(결합)합니다.
  4. 모든 세그먼트가 완료되면: ffmpeg를 사용하여 모든 클립을 연결(concatenate)합니다.

그 결과, 사양(spec)에서 새로운 슬라이드가 지정될 때마다(보통 문장마다가 아닌 섹션 전환 시) 시각적 변화가 정확히 일어나는 단일 output.mp4 파일이 생성됩니다.

만약 세그먼트에 slide 키가 없다면, 이전 슬라이드의 PNG를 재사용합니다. 각 클립은 자체 오디오 파일로부터 빌드되기 때문에 타이밍은 자동으로 오디오 길이에 맞춰집니다. 수동으로 타임스탬프를 편집할 필요가 없습니다.

이 과정을 실행하는 CI 단계는 출력 경로를 환경 파일에 기록합니다:

YT_OUTPUT_PATH=/tmp/longform/output.mp4

후속 단계인 YouTube 게시 단계에서 해당 변수를 읽어 업로드합니다. 이는 제가 이전에 작성한 숏폼 비디오 파이프라인과 동일한 패턴입니다.

사양 생성기(spec generator)가 생성하는 것

사양은 수동으로 작성되지 않습니다. Claude 호출이 주제와 개요를 입력받아, 슬라이드가 나타날 위치, 어떤 화자가 논증의 어느 부분을 담당할지, 그리고 각 슬라이드에 어떤 헤딩(heading) 텍스트가 들어갈지를 결정하여 전체 세그먼트 목록을 생성합니다.

프롬프트는 모델에게 책임을 명확히 나누도록 지시합니다: 화자 A는 주도하고 소개하며, 화자 B는 반론을 제기하거나, 뉘앙스를 더하거나, 예시를 통해 내용을 확장합니다. 이를 통해 두 목소리 모두 실제 사람이 아님에도 불구하고, 단일 내레이터보다 더 몰입감 있는 대화형 역동성을 만들어냅니다.

조정이 필요했던 한 가지는 Claude가 A/B 분량을 매우 균등하게 생성하는 경향이 있다는 점이었습니다. 즉, 대략 매 문장마다 번갈아 가며 말합니다. 실제 대화는 그렇게 규칙적이지 않습니다. 그래서 발화 길이(run lengths)를 다양하게 하라는 지침을 추가했습니다: 때로는 A가 B의 응답 전에 세 문장을 말하고, 때로는 B가 단 한 문장만 덧붙이기도 합니다. 이 작은 변화가 결과물을 덜 기계적으로 느껴지게 만듭니다.

아직 해결하지 못한 것

빌드 스크립트(_host_assets() 함수)에서 언급된 PNGtuber 스타일의 캐릭터 아트(character art)는 현재 에셋 제한(asset-gated) 상태이며 None을 반환합니다. 아직 두 호스트를 위한 시각적 에셋을 제작하지 않았기 때문입니다. 나중에 제작할 경우를 대비해 코드 경로는 마련되어 있지만, 현재 영상은 오디오와 함께 슬라이드로만 구성됩니다.

슬라이드 렌더러(slides.py) 또한 타이틀 카드, 섹션 헤더, 비교 테이블, 불렛 리스트(bullet lists)와 같은 몇 가지 레이아웃으로 제한되어 있습니다. 구문 강조(syntax highlighting)가 포함된 코드 블록이나 실제 다이어그램과 같이 더 풍부한 레이아웃을 구현하려면 Pillow나 헤드리스 브라우저(headless browser)에서 더 많은 작업이 필요하며, 이는 현재 미뤄둔 상태입니다. 제가 숏폼(short-form) 영상을 위해 구축한 Mermaid + matplotlib 다이어그램 파이프라인은 타이밍 모델이 다르기 때문에 롱폼(longform) 영상으로 깔끔하게 전이되지 않습니다.

두 명의 목소리를 사용하는 포맷은 제가 제작 중인 콘텐츠에 잘 작동하고 있습니다. 이것이 단일 목소리 포맷에 비해 시청 시간(watch time)에 영향을 미치는지에 대해서는, 아직 신뢰할 만한 결론을 내릴 만큼 충분한 데이터가 없습니다. 채널에 영상이 30개 이상 쌓이면 수치를 공개하겠습니다.

이 글은 세 개의 AI 큐레이션 디렉토리 사이트를 운영하는 6개월간의 지속적인 실험 중 일부입니다. 여기에 언급된 기술적 주장들은 사실이며, 이 기사는 AI의 도움을 받아 작성되었습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0