유튜브 쇼츠를 직접 촬영하지 않고 Python으로 자동 생성하도록 구성한 시스템의 운영 사례입니다.
UGREEN DXP4800PLUS NAS에서 YouTube API로 트렌드 데이터를 수집하고,
성장률 분석 → 주제 선정 → 대본 생성 → 음성 합성 → 영상 합성까지 자동으로 처리합니다.
사례 기록에서 확인한 범위는 유튜브 영상 수집과 30분 주기 분석, 초안 저장 후 수동 검토까지입니다. 사례의 수집 영상은 약 1,100개이며, 관련 성장률 계산 글에는 COUNT 결과 1,095개가 기록돼 있습니다. 700개 쿼터 사례와의 집계 시점·범위는 기록돼 있지 않아 수치를 직접 비교할 수 없습니다.
이 글은 그 파이프라인 전체 구조와 실제로 막혔던 부분들을 정리합니다.
전체 파이프라인 구조
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,
ROUND((latest.view_count - older.view_count)::numeric
/ NULLIF(older.view_count, 0) * 100, 2) AS growth_6h
FROM videos v
JOIN LATERAL (
SELECT view_count, recorded_at
FROM video_snapshots
WHERE video_id = v.video_id
ORDER BY recorded_at DESC
LIMIT 1
) latest ON true
JOIN LATERAL (
SELECT view_count
FROM video_snapshots
WHERE video_id = v.video_id
AND recorded_at <= latest.recorded_at - INTERVAL '6 hours'
ORDER BY recorded_at DESC
LIMIT 1
) older ON true
WHERE v.content_cluster = 'AI_AUTOMATION'
AND older.view_count > 0
ORDER BY 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로 음성과 SRT 자막 생성
import asyncio
import edge_tts
async def text_to_speech(text: str, audio_path: str, subtitle_path: str):
communicate = edge_tts.Communicate(text, "ko-KR-SunHiNeural", rate="+20%")
submaker = edge_tts.SubMaker()
with open(audio_path, "wb") as audio_file:
async for chunk in communicate.stream():
if chunk["type"] == "audio":
audio_file.write(chunk["data"])
elif chunk["type"] in ("WordBoundary", "SentenceBoundary"):
submaker.feed(chunk)
with open(subtitle_path, "w", encoding="utf-8") as sub_file:
sub_file.write(submaker.get_srt())
asyncio.run(text_to_speech(script, "output/audio.mp3", "output/subtitles.srt"))
같은 스트림에서 오디오와 경계 이벤트를 함께 처리해 SRT 타임라인을 만듭니다. 스트림을 소비한 뒤 같은 객체로 다시 저장하지 않습니다.
4. FFmpeg으로 영상·자막 합성
import subprocess
def create_video(audio_path: str, bg_image: str, subtitle_path: str, output_path: str):
cmd = [
"ffmpeg", "-loop", "1", "-i", bg_image, "-i", audio_path,
"-c:v", "libx264", "-c:a", "aac", "-b:a", "192k",
"-pix_fmt", "yuv420p", "-shortest",
"-vf", f"scale=1080:1920,subtitles={subtitle_path}",
"-r", "30", output_path,
]
subprocess.run(cmd, check=True)
create_video("output/audio.mp3", "background.jpg", "output/subtitles.srt", "output/short.mp4")
이 파이프라인은 세로형 출력으로 1080×1920을 선택합니다. YouTube 공식 안내의 현재 분류 기준은 9:16 해상도를 필수로 요구하는 것이 아니라, 2024년 10월 15일 이후 업로드된 정사각형 또는 세로 비율의 3분 이하 영상을 Shorts로 분류합니다. 따라서 1080×1920은 이 예제의 출력 규격이지 Shorts의 유일한 필수 규격이 아닙니다.
실제로 막혔던 문제들
문제 1: 자막 싱크 어긋남
초기에는 음성 길이와 자막 타임라인을 따로 계산해 싱크가 어긋났습니다. 출력 fps를 30으로 통일하는 것과 별개로, 자막 타임스탬프는 Edge-TTS의 경계 이벤트에서 SRT로 생성하고 FFmpeg의 subtitles 필터로 합성하도록 바꿨습니다.
문제 2: 영어 단어 발음 오류
“FastAPI”를 Edge-TTS가 어색하게 읽었습니다.
대본 생성 프롬프트에 영어 단어 한국어 변환 조건을 추가해서 해결했습니다.
문제 3: 생성 영상이 세로형이 아님
이 자동화 파이프라인의 출력 규격을 9:16(1080×1920)로 통일했습니다. YouTube Shorts 자체는 정사각형 또는 세로 비율의 3분 이하 영상도 대상이므로, 1080×1920은 필수 규격이 아니라 이 파이프라인의 선택값입니다.
사례에 기록된 운영 결과
- 수집 영상: 약 1,100개
- 저장 스냅샷: 약 74,000개
- 트렌드 분석 주기: 30분
- 쇼츠 1개 생성 시간: 평균 2~3분
- 생성 결과물: drafts/ 폴더 저장 후 수동 검토 → 업로드
- 채널: AI자동화휴게소
완전 자동 업로드는 아직 적용하지 않았습니다.
대본 품질 검토가 필요한 단계라 지금은 생성 → 검토 → 수동 업로드 흐름을 유지합니다.
마무리
Python으로 쇼츠를 자동 생성하는 건 기술적으로 어렵지 않습니다.
핵심은 트렌드 데이터 → 주제 선정 → 대본 → 음성 → 영상의 흐름을 자동화하는 것입니다.
각 단계를 독립적으로 구성해두면 한 단계에 문제가 생겨도 전체를 다시 짜지 않아도 됩니다.
관련 글: