21,000줄을 버리고 하루 만에 다시 만든 뉴스 숏폼 파이프라인

25개 템플릿에서 1개로, 과잉 설계를 단순화한 이야기

·10분 읽기·조회 23

TL;DR

  • 2달간 만든 25개 영상 포맷, 13개 씬 컴포넌트, 21,000줄의 코드를 하루 만에 버리고 단일 NewspaperTemplate으로 재구축
  • RSS 매체별 수집 방식을 웹검색 기반 "하나의 이슈, 네 개의 시각"으로 전환하여 콘텐츠 차별화 확보
  • Claude Code 에이전트 9개를 7단계 파이프라인으로 조율하는 구조 설계 -- 뉴스 수집부터 YouTube 업로드까지 자동화
  • Remotion 스튜디오로 실시간 프리뷰하며 대화형으로 디자인 조정하는 바이브코딩 워크플로우 정립

2달간 만든 21,000줄을 버린 이유

2월에 이 프로젝트를 시작했을 때, 야심 차게 설계했습니다. BreakingNews, KakaoChat, Documentary, Infographic 등 25개 영상 포맷 템플릿을 만들고, 13개의 모듈러 씬 컴포넌트를 조합하는 구조였습니다. 4개 채널(20대 좌/우, 5060 좌/우)마다 다른 디자인시스템을 적용하고, scene-director가 25개 포맷 중에서 적절한 걸 골라 화면을 연출하는 방식이었거든요.

문제는 이 구조가 너무 복잡해서 실제로 영상을 렌더링하기까지 도달하질 못했다는 겁니다.

과잉 설계된 파이프라인

2월의 파이프라인. 톱니바퀴가 너무 많았다.

실제로 만들었던 포맷들의 일부입니다:

속보 포맷 비교 포맷 커뮤니티 포맷 이모지 포맷 랭킹 포맷

속보, 여당vs야당 비교, 커뮤니티 게시판, 이모지 리액션, TOP5 랭킹 — 이 중 하나만 골라야 했다

scene-director 에이전트의 할 일이 너무 많았습니다. 25개 포맷의 특성을 이해하고, 4개 채널의 디자인시스템을 적용하고, 13개 씬 컴포넌트를 조합해야 했으니까요. 에이전트 하나가 감당하기엔 선택지가 너무 많았습니다.

씬 디렉터가 너무 할일이 많다.... 일단 형식을 하나로 통일해서 해보자.

이 한마디에서 오늘의 작업이 시작됐습니다.

결단: "기존꺼 무시하고 새로 만들자"

처음에는 25개 포맷 중 하나만 골라서 통일하려고 했습니다. 그런데 기존 코드를 살펴보니, 2달간 쌓인 추상화 레이어가 너무 두꺼웠습니다. SceneFrame, AppFrame, FormatFrame 같은 프레임 컴포넌트들, 채널별 테마 시스템, Zod 스키마 검증 레이어...

기존꺼 무시하고 새로만들자그냥

이 결정이 핵심이었습니다. 기존 코드 위에 덧붙이는 대신, 빈 캔버스에서 시작하기로 한 것입니다.

아카이브 작업은 빠르게 진행됐습니다. 기존 remotion/src/ 하위의 모든 파일을 _archive/remotion-v1/로 이동하고, 프리뷰 이미지와 테스트 영상도 정리했습니다. .gitignore_archive/를 추가해서 git 히스토리는 보존하되, 작업 디렉토리는 깨끗하게 비웠습니다.

git diff 기준으로 94개 파일이 변경되고, 21,619줄이 삭제됐습니다. 새로 추가된 건 383줄뿐이었습니다.

신문 한 장으로 충분했다

새 템플릿의 컨셉은 단순합니다. 신문 배경 PNG 위에 제목과 자막, 그리고 이미지 한 장.

+---------------------------+
|                           |
|   [빨간 둥근 박스: 제목]   |  <- 상단, 한 줄
|                           |
|      [뉴스 이미지]         |  <- 중앙
|                           |
|   [검은 반투명 자막 2줄]   |  <- 하단
|                           |
+---------------------------+
NewspaperTemplate 씬1 NewspaperTemplate 씬5

신문 배경 + 제목 배지 + 이미지 + 자막 2줄. 이게 전부다.

Remotion 스튜디오를 띄워놓고 실시간으로 확인하면서 디자인을 조정했습니다. 이 과정이 2시간 가까이 걸렸는데, 대부분 "쪼금만 더 내려", "사이즈 더 키워", "폰트 좀 줄여" 같은 미세 조정이었습니다.

재미있었던 건 자막 처리입니다. 처음에는 나레이션 한 문장을 3줄로 표시했는데, 너무 많은 텍스트가 화면을 가려서 2줄로 제한하기로 했습니다. 그런데 문장이 길면 잘리는 문제가 생겼거든요.

그 나레이션은 모두 나와야해. 자동으로 폰트를 살짝줄이도록해

이 요구사항을 fitToTwoLines 함수로 해결했습니다.

function fitToTwoLines(text: string): { lines: string[]; fontSize: number } {
  const options = [
    { fontSize: 60, charsPerLine: 18 },
    { fontSize: 52, charsPerLine: 21 },
    { fontSize: 46, charsPerLine: 24 },
    { fontSize: 40, charsPerLine: 28 },
  ];

for (const opt of options) { const lines = splitNarration(text, opt.charsPerLine); if (lines.length <= 2) { return { lines, fontSize: opt.fontSize }; } } // 최소 폰트에서도 안 되면 강제 2줄 return { lines: splitNarration(text, 28).slice(0, 2), fontSize: 40 }; }

60px부터 시도해서, 2줄에 안 들어가면 폰트를 줄이는 방식입니다. 이렇게 하면 짧은 문장은 크고 시원하게, 긴 문장은 자동으로 폰트가 작아지면서 모든 텍스트가 표시됩니다. 나레이션 원문이 잘리는 일은 없습니다.

뉴스 수집 방식도 바꿨다

기존 파이프라인은 RSS 피드에서 매체별로 기사를 수집했습니다. 네이버 랭킹 + 좌파 매체 2곳 + 우파 매체 2곳에서 각각 기사를 가져오는 방식이었습니다.

문제가 뭐였냐면, 매체별로 이미 편향된 기사를 고르는 거라 "같은 이슈를 4개 시각으로 비교"하는 게 안 됐습니다. A매체의 관세 기사와 B매체의 부동산 기사를 비교해봤자 의미가 없거든요.

뉴스 수집하지말고 그냥 웹검색해서 오늘의 뉴스들 중에서 4가지 진영으로 비교하면 괜찮을것같은 뉴스 선정하여 그 이슈에대해 정리. 이게 더 낫지?

이렇게 바꿨습니다. 하나의 이슈를 선정하고, 그 이슈에 대해 4개 진영이 어떻게 반응할지를 정리하는 구조로요. 같은 팩트를 가지고 young-left는 "국짐이 또 민생 발목 잡음" 프레이밍으로, young-right는 "26조 추경 까보니 선거용 현금살포" 프레이밍으로 풀어내는 겁니다.

실제로 오늘의 이슈 "26.2조 추경 고유가 피해지원금"으로 테스트해봤을 때, 4개 채널의 나레이션이 같은 숫자를 완전히 다르게 해석하는 걸 보면서 이 접근이 맞다는 걸 확인했습니다.

에이전트 팀 구성

오늘 가장 많은 시간을 쓴 건 에이전트들의 역할을 재정의하고 연결하는 작업이었습니다. 기존에는 8개 에이전트가 복잡하게 얽혀 있었는데, 역할을 명확하게 나누고 파이프라인 순서를 정했습니다.

Phase 1:  news-curator         뉴스 수집 + 이슈 선별
    |
Phase 2A: narration-writer     4채널 나레이션 생성
    |
    +---> Phase 2B: image-curator     Pexels 이미지 검색
    +---> Phase 3:  tts-producer      TTS 음성 생성
                |                        |
                v                        v
Phase 4:  newspaper-assembler  Root.tsx 조립
    |
Phase 5:  audio-director       BGM + SFX
    |
Phase 6:  video-renderer       Remotion 렌더링
    |
Phase 7:  youtube-uploader     업로드
파이프라인 흐름도

뉴스 수집 → 대본 → 이미지 → TTS → 영상 → 업로드

핵심은 Phase 2B와 3이 병렬로 돌아간다는 겁니다. 나레이션이 완성되면 이미지 검색과 TTS 생성을 동시에 시작할 수 있거든요. 이 두 작업은 서로 의존하지 않습니다.

각 에이전트는 Claude Code의 에이전트(Agent) 기능으로 구현되어 있습니다. .claude/agents/ 폴더에 각 에이전트의 역할, 입출력 형식, 허용된 도구가 마크다운으로 정의되어 있고, pipeline-director가 순서대로 호출합니다.

9개 에이전트, 각자의 역할

에이전트마다 "할 수 있는 일"이 명확하게 제한되어 있습니다. 웹검색이 필요한 에이전트에게만 WebSearch 도구를 주고, 파일을 생성해야 하는 에이전트에게만 Write 도구를 주는 식입니다. 이렇게 하면 에이전트가 자기 역할을 벗어나는 걸 구조적으로 막을 수 있습니다.

pipeline-director -- 총괄 디렉터입니다. Phase 1부터 7까지 순서대로 에이전트를 호출하고, 각 단계의 출력물이 다음 단계의 입력으로 제대로 전달되는지 확인합니다. Phase 2B(이미지)와 Phase 3(TTS)을 병렬로 디스패치하는 것도 이 에이전트의 몫입니다.

news-curator -- 뉴스를 수집하는 에이전트입니다. 웹검색으로 오늘의 정치 이슈를 파악하고, 후보 3~5개 중에서 "4개 진영이 가장 다르게 반응할 이슈"를 선정합니다. 선정 후에는 좌/우 매체 기사를 모두 읽고 팩트를 정리합니다. 기사에 포함된 이미지도 함께 수집합니다.

narration-writer -- 이 파이프라인의 핵심 에이전트입니다. 같은 팩트를 4개 채널에 맞게 완전히 다른 나레이션으로 변환합니다. 프레이밍 가이드, 채널별 페르소나(말투, 슬랭, 감정 곡선), 예시 대본을 참조해서 "한 문장 = 한 씬" 규칙으로 스크립트를 생성합니다. 이 에이전트가 어떻게 다른 결과를 내는지는 아래 "하나의 뉴스, 네 개의 시각" 섹션에서 자세히 다룹니다.

image-curator -- 나레이션의 각 씬에 어울리는 이미지를 찾는 에이전트입니다. Pexels API로 무료 이미지를 검색하는데, 한국어 키워드를 영어로 변환하고, 세로형(1080x1920) 영상에 맞는 해상도로 필터링합니다.

tts-producer -- Typecast API로 채널별 목소리를 생성합니다. 채널마다 다른 보이스(voice_id), 감정, 템포를 사용합니다. 20대 여성 목소리(Arin), 20대 남성 목소리(Wonwoo), 50대 남성 목소리(Ron, Chad) 등 채널 성격에 맞는 보이스가 할당되어 있습니다.

newspaper-assembler -- 나레이션 JSON, TTS 타이밍 정보, 이미지 경로를 합쳐서 Remotion의 Root.tsx 코드를 자동으로 생성합니다. 각 씬의 시작/종료 프레임, 자막 텍스트, 이미지 배치를 코드로 만들어내는 에이전트입니다.

audio-director -- 각 씬의 감정에 맞는 효과음(SFX)과 배경음악(BGM)을 선정합니다. "충격적인 팩트 공개" 씬에는 임팩트 사운드를, "감정적 호소" 씬에는 잔잔한 BGM을 배치하는 식입니다.

video-renderer -- Remotion CLI로 최종 MP4를 렌더링합니다. 1080x1920 해상도, 30fps, H.264 코덱으로 출력합니다. 채널 4개분을 순차적으로 렌더링합니다.

실제로 오늘 이 파이프라인을 돌려봤습니다. news-curator가 웹검색으로 이슈를 선정하고, narration-writer가 4채널 나레이션을 쓰고, tts-producer가 Typecast API로 음성을 생성하고, newspaper-assembler가 Root.tsx를 자동으로 생성하는 것까지 확인했습니다.

하나의 뉴스, 네 개의 시각

이 파이프라인의 진짜 차별점은 여기에 있습니다. 같은 뉴스를 4개 채널에서 완전히 다르게 풀어낸다는 것입니다. 단순히 말투만 바꾸는 게 아니라, 같은 숫자를 어떤 맥락에 놓느냐에 따라 전혀 다른 이야기가 됩니다.

4개 채널

채널 타겟 TTS 목소리
young-left (참다참다) 20대 진보 여성 공감+분노+SNS체, 반말 20대 여성 (Arin)
young-right 20대 보수 남성 조롱+팩트+직설, 반말 20대 남성 (Wonwoo)
senior-left 5060 진보 원칙+역사+경어, 존댓말 50대 남성 (Ron)
senior-right 5060 보수 단호+위기감+애국, 존댓말 50대 남성 (Chad)

20대 채널은 반말, 5060 채널은 존댓말을 씁니다. 같은 진보 성향이라도 20대는 SNS체로 분노를 표현하고, 5060은 원칙과 역사를 들어 격식 있게 비판합니다. 목소리도 다릅니다. 채널마다 다른 Typecast 보이스를 사용해서, 들리는 순간 "이건 누구 시점의 뉴스구나"가 바로 느껴지도록 설계했습니다.

실제 나레이션 비교: "26.2조 추경안"

오늘 파이프라인을 실제로 돌려서 나온 결과입니다. 이슈는 "26.2조 추경안, 여야 '고유가 피해지원금' 놓고 정면충돌"이었습니다.

young-left (참다참다) -- 제목: "국짐이 또 민생 발목 잡음"

"아니 기름값 미쳤는데 국짐이 지원금을 막겠대ㅋㅋ" "지금 호르무즈 해협 막혀서 유가가 폭등 중이거든?"

여당을 "국짐"이라 부르고, "ㅋㅋ"을 붙이면서 조롱과 분노를 동시에 표현합니다. 유가 폭등의 원인(호르무즈 해협)을 먼저 설명해서 "정부가 도와야 하는 상황"이라는 프레임을 깝니다.

young-right -- 제목: "26조 추경 까보니 선거용 현금살포"

"자 보세요, 26조 추경 까봤더니 실화임." "근데 소득 하위 70%면 사실상 전국민 현금살포 아님?"

같은 26.2조라는 숫자를 "까봤더니"라는 표현으로 해체합니다. "하위 70% = 사실상 전국민"이라는 계산을 들이대면서 "선거용 퍼주기"라는 프레임을 씌웁니다.

senior-left -- 제목: "국민 보호가 매표입니까"

"국민 여러분, 지금 대한민국이 에너지 위기를 맞고 있습니다." "유가가 치솟고, 서민 생활이 직격탄을 맞고 있는 상황입니다."

존댓말에 격식체를 사용합니다. "에너지 위기", "직격탄"처럼 위기 상황을 강조하면서, 지원금을 막는 것이 "국민 보호를 매표로 폄하하는 것"이라는 도덕적 프레임을 세웁니다.

senior-right -- 제목: "26조 퍼주기, 나라 곳간이 비고 있습니다"

"국민 여러분! 이 정권이 또 26조를 퍼주겠다고 합니다!" "그런데 이 추경의 실체를 까보니 참으로 황당합니다."

같은 존댓말이지만 톤이 완전히 다릅니다. "퍼주겠다", "실체를 까보니"라는 표현으로 정부를 공격하면서, "나라 곳간"이라는 재정 위기 프레임을 내세웁니다.

같은 팩트, 다른 프레임

정리하면 이렇습니다. "26.2조 추경"이라는 똑같은 숫자가:

  • young-left: "기름값 미쳤는데 당연히 줘야지, 국짐이 막는 게 문제"
  • young-right: "26조 까봤더니 선거용 현금살포, 세금 낭비"
  • senior-left: "에너지 위기에 국민 보호는 당연한 의무"
  • senior-right: "26조 퍼주기로 나라 곳간이 바닥"

narration-writer 에이전트가 이렇게 다른 결과를 내는 원리는 세 가지입니다.

첫째, 프레이밍 가이드입니다. 진보 채널은 "감성/선악" 프레임으로, 보수 채널은 "팩트/데이터" 프레임으로 접근하도록 가이드가 설정되어 있습니다. 같은 숫자도 진보 쪽에서는 "서민이 이만큼 고통받고 있다"로, 보수 쪽에서는 "세금이 이만큼 낭비되고 있다"로 해석합니다.

둘째, 채널 페르소나입니다. 각 채널의 말투, 자주 쓰는 슬랭, 감정 변화 곡선이 다릅니다. young-left는 "ㅋㅋ", "ㄹㅇ" 같은 SNS체를, senior-right는 "국민 여러분!", "참으로 황당합니다" 같은 격양체를 사용합니다.

셋째, 팩트 강조/축소입니다. 같은 기사에서 어떤 팩트를 크게 다루고 어떤 팩트를 축소할지가 채널마다 다릅니다. 진보 채널은 "유가 폭등으로 서민이 고통"을 강조하고, 보수 채널은 "소득 하위 70% = 사실상 전국민"이라는 숫자를 강조합니다.

이게 이 파이프라인의 핵심입니다. 4개 영상을 따로 만드는 게 아니라, 하나의 팩트 세트에서 4개의 시각을 자동으로 분화시키는 것입니다.

TTS에서 배운 것

TTS 테스트 중 재미있는 발견이 있었습니다. Typecast의 감정 옵션 중 "sad"를 쓰면 음성 속도가 눈에 띄게 느려지고, 억양이 부자연스러워집니다.

그 새드는 하지말자 새드라고되어잇는거는 다시해

이건 뉴스 숏폼에서 특히 문제가 됩니다. 30~60초짜리 영상에서 한 문장이 느려지면 전체 템포가 무너지거든요. 이 피드백을 narration-writer 에이전트의 규칙에 반영했습니다. 앞으로 모든 채널에서 sad 감정은 사용하지 않습니다.

배운 것

바이브코딩 관점

과감하게 버리는 것도 전략입니다. 2달간 만든 21,000줄을 버리는 건 심리적으로 쉽지 않습니다. 하지만 복잡한 구조가 실제 결과물 생성을 막고 있다면, 단순한 구조로 다시 시작하는 게 더 빠릅니다. 오늘 하루 만에 뉴스 수집부터 영상 렌더링까지의 파이프라인을 실제로 돌려본 건, 2달 동안 못한 일이었습니다.

에이전트 역할은 최대한 작게 나눠야 합니다. scene-director에게 "25개 포맷 중 골라서 디자인시스템 적용하고 씬 조합해" 같은 복합적인 일을 시키니까 제대로 작동하지 않았습니다. 각 에이전트가 하나의 명확한 일만 하도록 쪼개니까 파이프라인이 돌아가기 시작했습니다.

Remotion 스튜디오 + 대화형 조정이 효율적입니다. 스튜디오를 띄워놓고 "쪼금만 내려", "폰트 키워" 같은 지시를 반복하면서 디자인을 잡는 게, 사전에 완벽한 디자인을 설계하고 한번에 구현하는 것보다 훨씬 빨랐습니다.

개발 관점

선택지를 줄이면 시스템이 작동합니다. 25개 포맷에서 1개로 줄이자 scene-director가 필요 없어졌고, 나머지 에이전트들의 역할도 명확해졌습니다. 때로는 기능을 빼는 것이 시스템을 완성하는 길입니다.

같은 이슈의 다른 해석이 콘텐츠 힘입니다. 4개 매체에서 각각 다른 기사를 가져오는 것보다, 하나의 이슈를 4개 시각으로 풀어내는 게 훨씬 강력합니다. 26.2조 추경이라는 같은 숫자가 "민생 지원"이 되기도 하고 "선거용 매표"가 되기도 하는 걸 보여주는 게 이 채널의 콘텐츠 차별화입니다.

TTS 감정 옵션은 실제로 들어봐야 합니다. 텍스트로는 "슬픈 톤"이 어울릴 것 같아도, 실제 음성에서는 속도 저하와 부자연스러운 억양이 생길 수 있습니다. 특히 숏폼처럼 템포가 중요한 포맷에서는 감정 옵션을 실제 출력으로 검증하는 게 필수입니다.

다음 단계

현재 파이프라인은 각 단계를 수동으로 실행하면서 검증하고 있습니다. 다음 목표는 /news-pipeline 커맨드 하나로 Phase 1부터 7까지 자동으로 돌아가게 만드는 것입니다. 그리고 채널별 색상 차별화 -- 지금은 모든 채널이 같은 빨간색 제목 배지를 쓰고 있는데, 채널 성격에 맞게 색상을 분리할 계획입니다.

2달간의 과잉 설계를 하루 만에 정리하고, 실제로 작동하는 파이프라인을 만든 하루였습니다. 때로는 덜 만드는 게 더 잘 만드는 길이라는 걸 다시 한번 느꼈습니다.