
로컬 TTS 서버를 운영한 지 꽤 됐다. Supertonic이라는 한국 회사(Supertone)가 만든 TTS 엔진을 FastAPI로 직접 감싸서 OpenAI 호환 /v1/audio/speech 엔드포인트로 만들어 쓰고 있었는데 — 예전에 썼던 Supertonic 3 업그레이드 후기 — 온디바이스 TTS 프록시를 v2에서 v3로 올리며 에서 다뤘던 그 프록시다 — v1.3.1부터 공식 serve CLI가 나오면서 모든 게 달라졌다.
원래 구조
예전에는 이렇게 생겼었다:
supertonic_openai_tts_proxy/
├── main.py # FastAPI 앱, 엔드포인트 정의
├── schemas.py # Pydantic 요청 모델
├── synth.py # supertonic.TTS 직접 호출
├── audio.py # ffmpeg 변환
└── frontend/ # 테스트용 플레이그라운드
synth.py에서 supertonic.TTS 객체를 직접 생성하고, 모델을 로딩하고, 음성을 합성해서 WAV로 내보내고, 다시 audio.py에서 ffmpeg로 mp3/opus로 변환했다.
처음에는 재미있었다. TTS 엔진을 직접 다루니까 내부 동작도 이해되고 제어할 수 있다는 만족감도 있었다.
다만 시간이 지나면서 몇 가지 아쉬운 점이 눈에 띄기 시작했다.
한계
- Supertonic 버전 올릴 때마다 내 코드도 고쳐야 함 — API 변경이 생기면 synth.py부터 다시 확인
- 내가 만든
/v1/audio/speech밖에 없음 — health 체크, 음성 목록 조회, 배치 합성 같은 건 직접 만들기는 귀찮아서 안 만듦 - 프론트엔드 플레이그라운드도 내가 직접 만듦 — TTS 서비스를 만드는 게 본업은 아니라서
- 유지보수 포인트가 계속 쌓임 — 시간이 갈수록 신경 써야 할 부분이 늘어남
공식 serve 등장
Supertonic v1.3.1부터 supertonic serve라는 CLI가 생겼다. pip install 'supertonic[serve]' 한 방으로 실행되는, 완전한 OpenAI 호환 /v1/audio/speech 서버였다.
게다가 내가 안 만든 것들도 다 있었다:
/v1/health— 상태 체크/v1/styles— 음성 목록/v1/tts— 네이티브 합성/v1/tts/batch— 배치 합성/docs— Swagger 문서
딱 하나 빠진 게 있었는데, 바로 mp3/opus 출력이었다. 공식 serve가 지원하는 포맷은 wav, flac, ogg(Vorbis) 뿐이었다. 문제는 텔레그램이 opus를 쓰고, 일반적인 TTS 클라이언트들이 mp3를 자주 요청한다는 점.
그래서 바꾼 구조
serve와 커스텀 프록시를 완전히 분리했다.
supertonic serve (:8788) ← 공식 serve (Supertone이 관리)
↕
supertonic-proxy (:8789) ← 130줄 ffmpeg 변환 프록시 (내가 관리)
↕
Hermes / OpenAI 클라이언트
supertonic serve는 8788번 포트에서 내부 백엔드로만 띄움- 8789번 프록시는 들어오는 요청 중
/v1/audio/speech만 가로채서 WAV로 받은 뒤 ffmpeg로 변환 - 그 외 모든 요청(/v1/health, /v1/styles, /v1/tts 등)은 그냥 통과(패스스루)
프록시 코드는 직접 짰지만 단 130줄. httpx + FastAPI + ffmpeg 서브프로세스 호출이 전부다.
핵심 로직만 보면:
if fmt in ("wav", "flac", "ogg"):
# 공식 serve가 네이티브로 지원 — 그냥 통과
return await forward(body)
# 나머지(mp3, opus, aac): WAV로 받아서 ffmpeg 변환
wav = await request_wav(body)
return await ffmpeg_convert(wav, fmt)
이게 끝이다. 변환이 필요 없는 포맷은 serve가 직접 처리하고, 필요한 포맷만 WAV를 받아서 바꿔준다.
뭐가 좋아졌나
| 항목 | 전 | 후 |
|---|---|---|
| TTS 엔진 코드 | 내가 직접 호출 | serve가 다 해줌 |
| 유지보수 | synth.py 등 4개 모듈 | serve는 Supertone한테 맡김 |
| 엔드포인트 | /v1/audio/speech 하나 | health, styles, batch, docs 전부 |
| 프록시 코드 | 복잡 | 130줄 (ffmpeg 변환만) |
| 포맷 지원 | mp3/opus/wav | serve 네이티브(wav/flac/ogg) + 프록시(mp3/opus/aac) |
제일 큰 변화는 "써드파티 TTS 서버 운영"에서 "공식 serve 앞단에 변환기 하나 붙인" 걸로 지위가 바뀐 점이다. 예전엔 Supertonic이 업데이트되면 내 코드도 같이 고쳐야 했는데, 지금은 그냥 pip install --upgrade supertonic[serve] 한 방이다.
근데 왜 굳이
"그냥 serve만 써도 되지 않냐?" — 맞다. wav/flac/ogg만 써도 되는 환경이면 serve 하나로 끝이다.
나는 Hermes Agent 와 텔레그램을 연결해서 쓰고있는데, 이 음성메시지가 opus 포멧으로 해야 올라간다.
변환 단계가 하나 더 끼면:
- 포트 하나 더 씀 (8788, 8789)
- ffmpeg 서브프로세스 오버헤드 (작지만 무시할 순 없음)
- 고장 포인트가 하나 더 생김
대신 mp3/opus/aac를 쓸 수 있고, serve가 커버 못 하는 포맷을 유연하게 추가할 수 있다. 트레이드오프는 명확하다.
'Study > AI' 카테고리의 다른 글
| Supertonic 3 업그레이드 후기 — 온디바이스 TTS 프록시를 v2에서 v3로 올리며 (0) | 2026.05.16 |
|---|---|
| Hermes Agent core 수정 없이 Supertonic2 TTS 붙이기 (0) | 2026.04.24 |
| insane-search-hermes를 공개했습니다 (0) | 2026.04.22 |
| Google AI Studio, 구독만으로 더 넓어진 실험 한도 (0) | 2026.04.21 |
| 한국어 TTS 비교 후기: EdgeTTS를 거쳐 Supertonic-2로 정착하기까지 (0) | 2026.04.16 |