유튜브 쇼츠를 Python으로 자동 생성하는 방법 (실제 운영 중인 자동화 파이프라인 공개)

유튜브 쇼츠를 직접 촬영하지 않고 Python으로 자동 생성하는 시스템을 실제로 운영하고 있다. UGREEN DXP4800PLUS NAS에서 YouTube API로 트렌드 데이터를 수집하고, 성장률 분석 → 주제 선정 → 대본 생성 → 음성 합성 → 영상 합성까지 자동으로 처리한다.

현재 약 1,100개 영상 데이터를 수집 중이며, 30분마다 트렌드 분석을 수행한다. 이 글은 그 파이프라인 전체 구조와 실제로 막혔던 부분들을 정리한다.


전체 파이프라인 구조

YouTube API
↓
PostgreSQL (영상 데이터 저장)
↓
성장률 분석 (6시간 / 24시간)
↓
AI 언어 모델 (대본 생성)
↓
Edge-TTS (한국어 음성 합성)
↓
FFmpeg (자막 + 배경 합성)
↓
drafts/ 폴더 저장
↓
수동 검토
↓
YouTube 업로드

각 단계가 독립적인 Python 스크립트로 구성돼 있어서 문제가 생긴 단계만 수정하고 나머지는 그대로 쓸 수 있다.


1. PostgreSQL에서 트렌드 주제 선정

단순히 조회수 높은 영상이 아니라 6시간 성장률 기준으로 지금 뜨는 주제를 고른다. 성장률 계산 구조는 이전 글 PostgreSQL로 YouTube 조회수 성장률 계산하기에서 다뤘다.

import asyncpg

async def get_trending_topics():
    conn = await asyncpg.connect(DATABASE_URL)
    rows = await conn.fetch("""
        SELECT v.title, v.content_cluster, s.growth_6h
        FROM videos v
        JOIN video_snapshots s ON v.video_id = s.video_id
        WHERE s.recorded_at = (SELECT MAX(recorded_at) FROM video_snapshots)
          AND s.growth_6h IS NOT NULL
          AND v.content_cluster = 'AI_AUTOMATION'
        ORDER BY s.growth_6h DESC
        LIMIT 5
    """)
    await conn.close()
    return [dict(r) for r in rows]

content_cluster = 'AI_AUTOMATION'으로 필터링해서 채널 방향성과 맞는 주제만 고른다.


2. AI 언어 모델로 대본 생성

주제가 정해지면 AI 언어 모델로 60초 분량의 쇼츠 대본을 생성한다.

import httpx, os

async def generate_script(topic: str) -> str:
    # OpenRouter를 통해 AI 언어 모델 호출 (모델명은 env로 관리)
    api_key = os.getenv("OPENROUTER_API_KEY")
    prompt = f"""
다음 주제로 유튜브 쇼츠 대본을 작성해줘.
주제: {topic}

조건:
- 60초 분량 (약 250자)
- 첫 3초 안에 핵심 내용 언급
- 구어체로 작성
- 영어 단어는 한국어 발음으로 변환 (API → 에이피아이)
"""
    async with httpx.AsyncClient(timeout=30) as client:
        resp = await client.post(
            "https://openrouter.ai/api/v1/chat/completions",
            headers={"Authorization": f"Bearer {api_key}"},
            json={
                "model": os.getenv("SCRIPT_MODEL"),
                "messages": [{"role": "user", "content": prompt}],
                "max_tokens": 500,
            },
        )
    return resp.json()["choices"][0]["message"]["content"]

영어 단어를 한국어 발음으로 변환하는 조건이 중요하다. Edge-TTS가 영어 단어를 만나면 발음이 어색해지기 때문이다. 이 부분은 이전 글 Edge-TTS 한국어 음성 생성 시 발음 오류와 자막 싱크 문제 해결 방법에서 자세히 다뤘다.


3. Edge-TTS로 음성 합성

import edge_tts
import asyncio

async def text_to_speech(text: str, output_path: str):
    communicate = edge_tts.Communicate(
        text=text,
        voice="ko-KR-SunHiNeural",
        rate="-5%"
    )
    await communicate.save(output_path)

asyncio.run(text_to_speech(script, "output/audio.mp3"))

rate="+20%"는 실험 끝에 정착한 값이다. 기본 속도는 쇼츠용으로 약간 느리고, +20%가 약 4.2자/초로 60초 분량에 맞다.


4. FFmpeg으로 영상 합성

import subprocess

def create_video(audio_path: str, bg_image: str, output_path: str):
    cmd = [
        "ffmpeg",
        "-loop", "1",
        "-i", bg_image,
        "-i", audio_path,
        "-c:v", "libx264",
        "-tune", "stillimage",
        "-c:a", "aac",
        "-b:a", "192k",
        "-pix_fmt", "yuv420p",
        "-shortest",
        "-vf", "scale=1080:1920",
        "-r", "30",
        output_path
    ]
    subprocess.run(cmd, check=True)

-r 30으로 fps를 고정하는 게 중요하다. 소스 클립마다 fps가 25~29.97로 제각각이라 고정하지 않으면 자막 싱크가 어긋나는 문제가 생긴다.


실제로 막혔던 문제들

문제 1: 자막 싱크 어긋남

음성 길이와 자막 타임라인이 맞지 않았다. 원인은 fps 불일치였다. 소스마다 fps가 달라서 자막 타임코드 계산이 틀렸다. -r 30으로 고정 후 해결됐다.

문제 2: 영어 단어 발음 오류

"FastAPI"를 Edge-TTS가 어색하게 읽었다. 대본 생성 프롬프트에 영어 단어 한국어 변환 조건을 추가해서 해결했다.

문제 3: 생성 영상이 세로형이 아님

유튜브 쇼츠는 9:16 비율(1080x1920)이어야 한다. -vf scale=1080:1920을 추가해서 강제 변환했다.


실제 운영 결과

  • 수집 영상: 약 1,100개
  • 저장 스냅샷: 약 74,000개
  • 트렌드 분석 주기: 30분
  • 쇼츠 1개 생성 시간: 평균 2~3분
  • 생성 결과물: drafts/ 폴더 저장 후 수동 검토 → 업로드
  • 채널: AI자동화휴게소

완전 자동 업로드는 아직 적용하지 않았다. 대본 품질 검토가 필요한 단계라 지금은 생성 → 검토 → 수동 업로드 흐름을 유지한다.


마무리

Python으로 쇼츠를 자동 생성하는 건 기술적으로 어렵지 않다. 핵심은 트렌드 데이터 → 주제 선정 → 대본 → 음성 → 영상의 흐름을 자동화하는 것이다. 각 단계를 독립적으로 구성해두면 한 단계에 문제가 생겨도 전체를 다시 짜지 않아도 된다.

관련 글:

댓글

이 블로그의 인기 게시물

UGREEN DXP4800PLUS NAS로 유튜브 쇼츠 자동화 시스템 구축기 (Docker · FastAPI · PostgreSQL · Edge-TTS)

YouTube Data API 쿼터 초과를 피하는 방법 (700개 쇼츠 수집 사례)

FastAPI app.mount 사용 후 API가 404가 되는 이유와 해결 방법