도입
3편까지는 captionWords(자막이 무슨 단어이고 몇 프레임에 나오는지)를 사람이 직접 손으로 적었습니다.
대본만 바꾸면 영상이 바뀐다: Remotion 데이터 주입 구조 만들기 (Shorts 자동화 3편)
도입2편에서는 Remotion 프로젝트를 처음 만들고, 코드 안에 직접 적어놓은 값(제목 텍스트, 색상 등)으로 첫 영상을 뽑았습니다. 코딩 몰라도 따라하는 Remotion 첫걸음 (Shorts 자동화 2편)도입1편에서
blog.jdevy.com
이번 편에서는 텍스트만 넣으면 음성 파일과 자막 타이밍이 자동으로 나오는 스크립트를 만듭니다.

edge-tts가 뭔가요
edge-tts는 Microsoft Edge 브라우저에 내장된 무료 TTS(텍스트를 음성으로 바꾸는) 엔진을 파이썬에서 그대로 불러쓸 수 있게 만든 라이브러리입니다. 무료이면서도 한국어 뉴럴 보이스를 지원하고, "이 단어는 몇 초부터 몇 초까지 읽힌다"는 단어별 타이밍 정보(WordBoundary)까지 함께 줍니다. 자막을 프레임 단위로 정확히 맞추려면 이 정보가 꼭 필요합니다.
설치
pip3 install edge-tts --break-system-packages
macOS의 Homebrew Python은 PEP 668이라는 안전장치가 있어서 일반적인 pip install이 에러를 낼 수 있습니다. --break-system-packages는 "이 설치는 의도한 거니 그냥 진행해라"라는 확인 옵션입니다.
스크립트 작성: tts_captions.py
#!/usr/bin/env python3
"""
정데비의 작업실 - edge-tts 기반 TTS + 단어별 캡션 타이밍 생성 스크립트
"""
import asyncio
import json
import sys
from pathlib import Path
import edge_tts
FPS = 30
async def generate(text: str, voice: str, out_audio: Path, out_captions: Path, fps: int = FPS):
communicate = edge_tts.Communicate(text, voice, boundary="WordBoundary")
words = []
with open(out_audio, "wb") as audio_file:
async for chunk in communicate.stream():
if chunk["type"] == "audio":
audio_file.write(chunk["data"])
elif chunk["type"] == "WordBoundary":
start_sec = chunk["offset"] / 10_000_000
dur_sec = chunk["duration"] / 10_000_000
words.append({
"word": chunk["text"],
"startFrame": round(start_sec * fps),
"endFrame": round((start_sec + dur_sec) * fps) - 1,
})
duration_in_frames = (words[-1]["endFrame"] + 15) if words else fps
out_captions.write_text(
json.dumps(
{"captionWords": words, "durationInFrames": duration_in_frames},
ensure_ascii=False,
indent=2,
),
encoding="utf-8",
)
print(f"오디오 생성 완료: {out_audio}")
print(f"캡션 {len(words)}개 생성, 씬 길이: {duration_in_frames}프레임")
def main():
if len(sys.argv) < 3:
print('사용법: python tts_captions.py "텍스트" 출력파일명 [voice]')
sys.exit(1)
text = sys.argv[1]
name = sys.argv[2]
voice = sys.argv[3] if len(sys.argv) > 3 else "ko-KR-InJoonNeural"
Path("public").mkdir(exist_ok=True)
out_audio = Path("public") / f"{name}.mp3"
out_captions = Path(f"{name}-captions.json")
asyncio.run(generate(text, voice, out_audio, out_captions))
if __name__ == "__main__":
main()
이 스크립트가 하는 일을 한 줄로 요약하면: 텍스트를 edge-tts에 보내고, 돌아오는 음성 데이터는 mp3로 저장하고, 동시에 돌아오는 단어별 타이밍 정보(시작/종료 시간)를 초 단위에서 프레임 단위로 바꿔 JSON으로 저장합니다. "초 × fps"로 계산하는 이유는 3편에서 본 captionWords의 startFrame/endFrame이 프레임 단위였기 때문입니다.
실행
python3 tts_captions.py "안녕하세요, 정데비의 작업실입니다. 오늘은 리모션 자동화를 테스트해봅니다." scene1
텍스트에 쉼표나 마침표를 넣는 건 상관없지만, 텍스트 전체를 큰따옴표로 감싸야 한다는 점만 기억하면 됩니다.
이슈 — 처음 실행 시 캡션이 빈 배열로 나옴
위 명령을 그대로 실행했더니 아래와 같은 결과가 나왔습니다.
캡션 0개 생성, 씬 길이: 30프레임
원인은 Python edge-tts 라이브러리가 v7.2.3부터 boundary의 기본값이 WordBoundary에서 SentenceBoundary로 바뀌었기 때문입니다. 단어별이 아니라 문장 단위로 결과가 나오게 바뀐 것이죠. 위 코드는 이미 boundary="WordBoundary"를 명시해둔 상태입니다 — 이 옵션이 없으면 위 증상이 나타났을 거예요. edge_tts.Communicate(text, voice)처럼 boundary 지정을 빠뜨린 코드를 쓰고 있다면 반드시 boundary="WordBoundary"를 추가하세요.
재실행 결과
python3 tts_captions.py "안녕하세요, 정데비의 작업실입니다. 오늘은 리모션 자동화를 테스트해봅니다." scene1
# 오디오 생성 완료: public/scene1.mp3
# 캡션 7개 생성, 씬 길이: 207프레임
scene1-captions.json 실제 출력은 이렇습니다.
{
"captionWords": [
{ "word": "안녕하세요", "startFrame": 3, "endFrame": 29 },
{ "word": "정데비의", "startFrame": 35, "endFrame": 52 },
{ "word": "작업실입니다", "startFrame": 53, "endFrame": 83 },
{ "word": "오늘은", "startFrame": 110, "endFrame": 124 },
{ "word": "리모션", "startFrame": 125, "endFrame": 140 },
{ "word": "자동화를", "startFrame": 141, "endFrame": 158 },
{ "word": "테스트해보겠습니다", "startFrame": 160, "endFrame": 192 }
],
"durationInFrames": 207
}
2편에서 만든 SceneVideo 컴포지션에 이 데이터를 연결해보면, 오디오 길이와 자막 타이밍이 프레임 단위로 정확히 일치하는 걸 확인할 수 있었습니다. 예를 들어 40프레임째는 "정데비의" 구간(35~52)이라 그 자막이 화면에 떠 있는 게 맞습니다.
주의사항 / 흔한 실수
- edge-tts는 내 PC에서 음성을 만드는 게 아니라 Microsoft 서버와 통신해서 만듭니다. 인터넷 연결이 꼭 필요합니다.
- voice 이름을 잘못 쓰면 에러가 나는데, 정확한 목록은 edge-tts --list-voices 명령으로 확인할 수 있습니다.
- 텍스트 안에 큰따옴표를 넣어야 한다면 이스케이프하거나, 텍스트 전체를 작은따옴표로 감싸는 걸 추천합니다.
마무리
이번 편에서는 텍스트 한 줄로 한국어 음성과 단어별 자막 타이밍을 한 번에 뽑아내는 스크립트를 만들었습니다. 이제 음성과 자막은 자동으로 생기는데, 이미지는 여전히 사람이 직접 구해야 합니다. 다음 편에서는 GPT Image 2 API로 이미지까지 자동으로 만들어 첫 엔드투엔드 파이프라인을 완성합니다.
AI 이미지까지 연결해서 첫 자동 영상 완성하기 (Shorts 자동화 5편)
도입4편에서는 음성과 자막을, 3편에서는 씬 구조를 만들었습니다. 한국어 AI 목소리 무료로 쓰기: edge-tts 연동하기 (Shorts 자동화 4편)도입3편까지는 captionWords(자막이 무슨 단어이고 몇 프레임에
blog.jdevy.com
[핵심요약] edge-tts로 한국어 음성+단어별 자막 타이밍 자동 생성하기
'실전 자동화 튜토리얼' 카테고리의 다른 글
| AI 이미지까지 연결해서 첫 자동 영상 완성하기 (Shorts 자동화 5편) (0) | 2026.07.18 |
|---|---|
| 대본만 바꾸면 영상이 바뀐다: Remotion 데이터 주입 구조 만들기 (Shorts 자동화 3편) (0) | 2026.07.18 |
| 코딩 몰라도 따라하는 Remotion 첫걸음 (Shorts 자동화 2편) (0) | 2026.07.18 |
| Shorts 자동화 툴을 Vrew에서 Remotion으로 바꾸면서 생긴 일들 (0) | 2026.07.18 |
| 돈 한푼 안 쓰고 백엔드 만들기: Cloudflare+Supabase 실전 구조 (0) | 2026.07.17 |