Telnyx AI Inference, TTS 및 Cloud Storage를 활용한 AI 오디오북 내레이터 구축하기
요약
Telnyx의 AI Inference, TTS, Cloud Storage를 활용하여 텍스트를 오디오북으로 변환하는 Flask 애플리케이션 구축 가이드입니다. AI가 텍스트를 장 단위로 분할하고 감정 큐를 추가하면, TTS가 음성을 생성하고 클라우드 스토리지에 저장하는 파이프라인을 다룹니다.
핵심 포인트
- AI Inference를 통한 텍스트 분할 및 내레이션 큐(톤, 페이싱) 추가
- TTS를 활용한 일관된 목소리의 MP3 오디오 생성
- S3 호환 Cloud Storage를 이용한 오디오 파일 저장 및 서명된 URL 제공
- Flask 기반의 단일 엔드포인트 파이프라인 구현 방법 제시
원고를 내레이션이 포함된 오디오북으로 만드는 작업은 전통적으로 성우를 고용하고, 스튜디오 시간을 예약하며, 수정 시 몇 시간 동안 재녹음을 해야 한다는 점을 감수해야 함을 의미합니다. 이 가이드에서는 원문을 입력받아 AI Inference (AI 추론)를 사용하여 페이싱(pacing) 및 감정 큐(cue)와 함께 장(chapter) 단위로 분할하고, Text-to-speech (TTS, 음성 합성)로 각 장을 내레이션하며, 생성된 MP3 파일을 재생 가능한 서명된 URL(presigned URL)과 함께 Telnyx Cloud Storage에 저장하는 Flask 앱을 구축합니다.
표준 코드 예제는 Telnyx 코드 예제 리포지토리에 있습니다:
https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-audiobook-narrator-python
이 예제가 구축하는 것
5개의 엔드포인트를 가진 단일 파일 Flask 앱입니다:
| Method | Path | Purpose |
|---|---|---|
POST | /books/narrate | 오디오북 내레이션을 위한 텍스트 제출 (메인 파이프라인) |
| ... |
파이프라인은 세 단계로 실행됩니다. AI Inference가 제출된 텍스트를 장 단위로 나누고 내레이션 큐(톤, 페이싱, 강조)를 추가합니다. Text-to-speech는 일관된 목소리를 사용하여 각 장을 MP3로 렌더링합니다. Cloud Storage는 최종 오디오를 보관하고 각 장에 대해 시간 제한이 있는 서명된 GET URL을 반환하여, 호출자가 장기적인 자격 증명을 처리하지 않고도 결과물을 스트리밍할 수 있도록 합니다.
이것이 중요한 이유
오디오북 내레이터는 단일 요청 흐름 내에서 세 가지 Telnyx AI 제품인 AI Inference, TTS, Cloud Storage를 연습하며, 텍스트 완성 대신 실질적인 결과물(실제 MP3 파일)을 반환합니다. 이는 출력이 텍스트가 아닌 미디어인 콘텐츠 생성 파이프라인을 구축하려는 모든 사람에게 유용한 참고 자료가 됩니다.
개발자들은 이 패턴을 다음과 같은 기반으로 사용합니다:
-
자가 출판 도구 — 오디오를 요구하는 배포 플랫폼을 위해 원고를 오디오북으로 변환
-
접근성 서비스 — 시각 장애인 독자를 위해 쓰인 콘텐츠를 오디오로 변환
-
팟캐스트 프리 프로덕션 (Podcast pre-production) — 인간 호스트가 나중에 다시 녹음하거나 리믹스할 수 있는 초안 내레이션 트랙 생성
-
다국어 내레이션 (Multi-language narration) — 현지화된 오디오를 위해 동일한 텍스트를 다양한 TTS 음성과 언어로 실행
사용된 제품 (Products Used)
telnyx_products: [AI Inference, TTS, Cloud Storage]
Telnyx AI Inference는 채팅 완성 (chat completions)을 위해 OpenAI 호환 API를 제공합니다. Telnyx TTS는 POST /v2/ai/generate를 통해 텍스트로부터 음성을 생성합니다. Telnyx Cloud Storage는 S3 호환 방식이므로, AWS SDK (boto3)를 통해 직접 통신할 수 있습니다. 이때 Telnyx API 키는 액세스 키 (access key)와 비밀 키 (secret key)로 모두 사용되며, 엔드포인트 호스트는 리전 범위로 지정됩니다 (https://{region}.telnyxcloudstorage.com).
아키텍처 (Architecture)
POST /books/narrate
|
v
...
코드 (The Code)
전체 앱은 단일 app.py 파일 내에 242줄로 구성되어 있습니다. 주요 부분은 다음과 같습니다:
설정 및 스토리지 클라이언트 (Configuration and storage client). Telnyx API 키는 환경 변수에서 로드되며 REST API (Inference, TTS)와 S3 클라이언트 (Cloud Storage) 모두에 사용됩니다. S3 클라이언트는 s3v4 서명을 사용하여 리전 범위의 Telnyx 엔드포인트를 가리킵니다:
import os, boto3
from botocore.config import Config
...
AI Inference 호출 (AI Inference call). 시스템 프롬프트는 모델이 텍스트를 장(chapter) 단위로 나누고 chapter_number, chapter_title, narration_text, tone, pacing 필드를 포함하는 JSON 배열을 반환하도록 지시합니다. 응답은 JSON으로 파싱되며, 모델이 잘못된 형식의 JSON을 반환할 경우 문단 분할 (paragraph splitting) 방식으로 대체(fallback)됩니다:
def inference(messages, max_tokens=4000):
resp = requests.post(f"{API}/ai/chat/completions", headers=HEADERS, json={
"model": AI_MODEL, "messages": messages,
...
TTS 생성 (TTS generation). 각 장의 내레이션 텍스트는 선택된 음성 및 output_format: mp3 설정과 함께 POST /v2/ai/generate로 전송됩니다. 응답 본문은 가공되지 않은 MP3 바이트 (raw MP3 bytes)입니다:
def tts_generate(text, voice=None):
resp = requests.post(f"{API}/ai/generate", headers=HEADERS, json={
"model": TTS_MODEL, "voice": voice or DEFAULT_VOICE,
...
Cloud Storage로 업로드. 각 장(chapter)의 MP3 파일은 put_object를 통해 업로드되며, 이후 1시간 동안 유효한 사전 서명된 GET URL (presigned GET URL)이 반환됩니다. 이를 통해 호출자는 장기적인 자격 증명(credentials)을 노출하지 않고도 오디오를 스트리밍할 수 있습니다:
def upload_to_storage(bucket, key, data, content_type="audio/mpeg"):
s3.put_object(Bucket=bucket, Key=key, Body=data, ContentType=content_type)
return s3.generate_presigned_url(
...
narrate 엔드포인트. 세 가지 단계를 하나로 연결하며, AI 청킹 (chunking) 결과가 잘못된 형식의 JSON을 반환할 경우 문단 단위로 텍스트를 분할하는 우아한 폴백 (graceful fallback) 메커니즘을 갖추고 있습니다:
@app.route("/books/narrate", methods=["POST"])
def narrate_book():
data = request.get_json() or {}
...
실행하기
git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/ai-audiobook-narrator-python
cp .env.example .env # TELNYX_API_KEY를 추가하고, 선택적으로 AI_MODEL, TTS_MODEL, BUCKET_NAME을 조정하세요
...
서버는 http://localhost:5000에서 시작됩니다. Telnyx Portal에서 audiobooks라는 이름의 버킷을 생성하거나 (또는 BUCKET_NAME을 기존 버킷으로 설정), 텍스트 청크를 제출하세요:
curl -X POST http://localhost:5000/books/narrate \
-H "Content-Type: application/json" \
-d '{
...
응답에는 book_id와 각 장마다 하나씩 할당된 사전 서명된 storage_urls 목록이 포함됩니다:
{
"book_id": "book-a1b2c3d4",
"title": "The Future of Infrastructure",
...
브라우저에서 storage_url을 열거나 클라이언트에서 스트리밍하여 내레이션된 장을 들어보세요.
사용 가능한 목소리
이 앱은 기본적으로 5가지 내레이터 목소리를 제공합니다:
| Key | Voice | Character |
|---|---|---|
warm_female | nova | 따뜻하고 표현력이 풍부함 — 기본 내레이터 |
| ... |
언제든지 다음 명령어로 목록을 확인할 수 있습니다:
curl http://localhost:5000/voices
/books/narrate 요청의 voice 필드에 원하는 목소리 식별자를 전달하세요.
프로덕션 환경으로 전환하기
이 예제는 도서 메타데이터를 위해 인메모리 딕셔너리 (in-memory dict)를 사용하며, 이는 로컬 테스트에는 적합하지만 재시작 시 상태가 손실됩니다. 프로덕션 (production) 환경을 위해서는 다음 사항을 고려하세요:
- 지속성 저장소 (Persistent storage) — 도서 메타데이터가 재시작 후에도 유지되도록 인메모리
books딕셔너리를 PostgreSQL 또는 Redis로 교체하세요. - 인증 (Authentication) — 모든 엔드포인트 (endpoint)에 API 키 검증을 추가하세요. 이 예제에는 인증 기능이 포함되어 있지 않습니다.
- 버킷 프로비저닝 (Bucket provisioning) — 첫 번째 요청을 보내기 전에 Telnyx Cloud Storage 리전 (region)에
BUCKET_NAME이 존재하는지 확인하거나, 시작 시create_bucket호출을 추가하세요. - 장문 입력 (Long-form input) — 청킹 (chunking) 단계에서 입력값이 15,000자로 잘립니다. 전체 원고의 경우, 클라이언트 측에서 소스 텍스트를 분할하고 각 섹션을 별도의 내레이션 (narrate) 요청으로 제출하세요.
- 사전 서명된 URL 수명 (Presigned URL lifetime) — URL은 한 시간 후에 만료됩니다. 더 긴 접근 시간이 필요한 경우, 저장된 객체 키 (object keys)로부터 필요할 때마다 새로운 사전 서명된 URL을 생성하거나, 퍼블릭 도메인 (public-domain) 콘텐츠의 경우 버킷을 퍼블릭 읽기 (public read)로 설정하세요.
- 웹훅 서명 검증 (Webhook signature verification) — 이 앱을 퍼블릭 URL을 통해 노출하는 경우, 들어오는 요청의 서명을 검증하세요 (여기서 보여주는 로컬 전용 흐름에는 필요하지 않습니다).
- 모니터링 (Monitoring) — 구조화된 로깅 (structured logging)을 추가하고
inference,tts_generate, 또는upload_to_storage호출 실패 시 알림을 설정하여 업스트림 (upstream) 문제를 조기에 발견하세요.
자주 묻는 질문 (Frequently Asked Questions)
이것을 실행하기 전에 기존의 Cloud Storage 버킷이 필요한가요?
네. Telnyx Portal에서 버킷을 생성하고 .env 파일의 BUCKET_NAME을 일치하도록 설정하세요. 앱이 자동으로 버킷을 생성해주지는 않습니다.
AI가 챕터 분할을 위해 잘못된 형식의 JSON을 반환하면 어떻게 되나요?
앱이 json.JSONDecodeError를 포착하여 이중 줄바꿈 문단 기준으로 텍스트를 분할하는 방식으로 대체합니다. 각 문단은 기본값인 tone: warm 및 pacing: moderate를 가진 챕터가 됩니다. 내레이션은 여전히 완료되지만, AI의 톤 (tone) 및 페이싱 (pacing) 지시사항은 적용되지 않습니다.
기본적으로 어떤 AI 모델이 사용되나요? 기본값은 moonshotai/Kimi-K2.6입니다 (.env.example에 설정되어 있으며 AI_MODEL을 통해 재정의할 수 있습니다). Telnyx AI Inference에서 사용 가능한 모든 모델이 작동합니다. 선택 가능한 옵션은 모델 목록 (models list)을 참조하세요.
챕터마다 다른 TTS 목소리를 사용할 수 있나요? 이 예제에서는 불가능합니다. voice는 요청당 한 번 설정되며 모든 챕터에 사용됩니다. 챕터별로 목소리를 다양화하려면, narrate_book 루프를 확장하여 AI의 응답에서 chapter["voice"]를 읽어온 뒤 이를 tts_generate에 전달하도록 수정해야 합니다.
미리 서명된 URL (presigned URLs)의 유효 기간은 얼마나 되나요? 한 시간입니다 (ExpiresIn=3600). 저장된 MP3 파일은 버킷(bucket)에 계속 유지되지만, URL만 만료됩니다. 새로운 URL이 필요할 때마다 s3.generate_presigned_url을 사용하여 새로운 미리 서명된 URL을 생성하세요.
업로드된 오디오는 공개되나요? 아니요. 버킷은 기본적으로 비공개(private) 상태입니다. 오직 미리 서명된 URL을 통해서만 제한된 시간 동안 읽기 권한이 부여됩니다. 오디오를 공개적으로 접근 가능하게 만들려면 (예: 퍼블릭 도메인 오디오북의 경우), Telnyx Portal에서 버킷에 공개 읽기 정책(public read policy)을 설정하세요.
최대 입력 텍스트 길이는 얼마인가요? 청킹 (chunking) 단계에서 입력값은 요청당 15,000자로 제한됩니다. 더 긴 원고의 경우, 클라이언트 측에서 소스를 섹션별로 분할하여 각각 별도의 /books/narrate 요청으로 제출한 다음, 결과로 나온 storage_urls를 하나로 합치세요.
영어 이외의 언어로 내레이션할 수 있나요? 네, Telnyx TTS는 여러 언어를 지원합니다. AI Inference 프롬프트는 영어로 되어 있지만, app.py의 시스템 메시지를 편집하여 다른 언어로 청킹하도록 설정하고, tts_generate에 해당 언어에 적합한 목소리를 전달할 수 있습니다.
리소스 (Resources)
리소스 (Resources)
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기