JavaScript TTS API: Node.js에서 MP3로 저장하기

서버의 JavaScript 코드에서 텍스트 음성 변환(TTS) API를 호출하세요. Node.js에서 Sume SDK로 텍스트를 제출하고, Job을 기다린 뒤 MP3를 저장합니다.

읽는 시간 5분Sume
전체 글

JavaScript로 텍스트 음성 변환(TTS)을 하려면 두 가지 방법 중 하나를 고르세요. 브라우저에서는 Web Speech API의 speechSynthesis가 텍스트를 소리 내어 읽으며, 보통 기기의 기본 음성 합성기를 거칩니다. 저장하거나 제공할 오디오 파일이 필요하다면 서버의 Node.js에서 호스팅된 텍스트 음성 변환 API를 호출하세요. 텍스트와 음성을 보내고, Job을 기다린 뒤, MP3를 내려받으면 됩니다. Sume의 TypeScript SDK로는 generateTtsV1(POST /v1/tts-1.0/generate)을 호출하고, waitForJob으로 기다린 다음, 오디오 파일을 fetch로 받습니다.

Sume 쪽 단계는 TypeScript SDK와 실행 기다리기 (영문) 문서, 그리고 Sume API 레퍼런스의 TTS 1.0 스키마에서 가져왔으며, 2026-09-27에 확인했습니다. 브라우저 API는 MDN의 Web Speech API 페이지를 바탕으로 설명합니다. curl로 정리한 TTS 요청 필드 전체는 텍스트 음성 변환 API에 있습니다.

왜 브라우저가 아니라 Node.js에서 텍스트 음성 변환을 호출하나요?

API 키를 서버에 두어야 하기 때문입니다. SDK 문서에 따르면 Sume 키는 크레딧을 소모하며, 브라우저에서 안전하게 쓸 수 있는 변형이 없습니다. 클라이언트 JavaScript, 모바일 번들, NEXT_PUBLIC_* 변수에 절대 넣지 마세요. 앞단에 자체 엔드포인트를 두고 거기서 Sume를 호출하세요. 웹 페이지가 API를 직접 호출하면 어떤 일이 생기는지는 CORS 가이드에서 다룹니다.

클라이언트는 npm install @sume-com/sdk로 설치하세요. fetch와 WebCrypto가 필요하며, Node 18 이상, Bun, Deno, Cloudflare Workers에서 동작합니다.

Node.js에서 텍스트를 MP3로 어떻게 변환하나요?

클라이언트를 하나 만들고, 텍스트를 음성과 함께 제출하고, Job을 기다린 뒤 파일을 내려받으세요. output_format을 지정하지 않으면 TTS 1.0은 44,100 Hz, 128 kbps의 MP3를 반환합니다. mode를 지정하지 않으면 제출은 async로 처리되며, waitForJob이 전제하는 제출 방식도 바로 이것입니다.

  • transcript는 1–20,000자를 받습니다. 음성은 목소리가 준비된 아바타의 avatar_id나 avatar_handle, 또는 이미 확보한 voice.id로 지정합니다. 음성을 찾는 방법은 텍스트 음성 변환 API에서 다룹니다. 영어가 아닌 텍스트에는 language를 지정하세요.
  • idempotency-key 헤더를 보내면 제출을 안전하게 재시도할 수 있습니다. 클라이언트는 이 헤더가 있는 POST만 재시도하며, 같은 키와 본문으로 재시도하면 두 번째 Job을 과금하는 대신 원래 Job을 반환합니다.
  • 생성된 오퍼레이션은 API 오류가 나도 throw하지 않고 { data, error, response }로 resolve하므로, 먼저 error를 확인하세요. waitForJob은 실패하거나 취소된 Job도 반환하므로, 결과를 읽기 전에 job.status를 확인하세요.
  • 오디오는 result.artifacts[]에서 type: "audio"인 항목입니다. 그 url은 공개 Sume CDN URL이므로 일반 fetch로 내려받을 수 있습니다.
import { writeFile } from "node:fs/promises";
import { createSumeClient, generateTtsV1, waitForJob } from "@sume-com/sdk";

const client = createSumeClient({ apiKey: process.env.SUME_API_KEY! });

const { data, error } = await generateTtsV1({
  client,
  headers: { "idempotency-key": "shipping-notice-001" },
  body: {
    transcript: "Your order has shipped. It should arrive on Thursday.",
    avatar_handle: "narrator",
    language: "en",
  },
});
if (error) throw new Error(JSON.stringify(error));

const job = await waitForJob(data!.data.request_id, { client });
if (job.status !== "completed") throw new Error(`TTS job ${job.status}`);

const audio = job.result?.artifacts?.find((a) => a.type === "audio");
const file = await fetch(audio!.url);
await writeFile("shipping-notice.mp3", Buffer.from(await file.arrayBuffer()));

TTS Job에는 어떤 SDK 호출을 쓰나요?

단계마다 SDK 호출 하나와 HTTP 경로 하나가 대응합니다. 생성된 함수는 OpenAPI 오퍼레이션 ID를 따라 이름이 붙으며, 전체 목록은 에디터의 자동완성에서 확인하라고 문서가 안내합니다. 대기 헬퍼의 옵션과 오류는 Sume Job이나 실행이 끝날 때까지 기다리기에서 다룹니다.

TypeScript SDK, 실행 기다리기 (영문), Job과 결과 (영문), Sume API 레퍼런스 기준, 2026-09-27 확인.
단계SDK 호출HTTP 경로알아 둘 점
제출generateTtsV1POST /v1/tts-1.0/generate응답에 Job ID가 request_id로 담김.
대기waitForJobGET /v1/jobs/:id/status폴링 사이에 최소 2초를 기다리고, next_poll_after_seconds가 요청하면 더 오래 기다림. 기본 타임아웃은 20분.
나중에 읽기getApiJobGET /v1/jobs/:id대기가 타임아웃되어도 Job은 취소되지 않음. 계속 실행되고 과금도 계속됨.
취소cancelApiJobPOST /v1/jobs/:id/cancel생성이 시작되기 전에만 성공. 그 뒤에는 Job이 끝까지 실행됨.

파일을 기다리는 대신 음성을 스트리밍할 수 있나요?

TTS 1.0으로는 할 수 없습니다. API 레퍼런스는 TTS 1.0을 폴링이나 웹훅으로 결과를 받는 비동기 Job이며 스트리밍 방식이 아니라고 설명합니다. mode: "sync"는 HTTP 요청을 최대 30초까지만 붙잡고 있고, /result는 result_ready가 true가 될 때까지 409 job_not_completed로 응답합니다. 그래서 async로 제출하고 waitForJob으로 기다리는 방법이 간단합니다.

폴링을 건너뛰려면 제출할 때 공개 HTTPS webhook_url을 함께 보내세요. 전달은 종료 상태(job.completed, job.failed, job.canceled)에서만 이뤄지고, 서명은 SDK의 verifyWebhook으로 확인하며(웹훅 검증), API 레퍼런스는 폴링도 백업으로 쓸 수 있게 남겨 두라고 안내합니다.

텍스트는 얼마나 길어도 되고, 비용은 얼마인가요?

요청 하나에 최대 20,000자까지 넣을 수 있으며, 합성된 오디오가 1,200초를 넘으면 tts_duration_exceeded로 실패하고 크레딧은 확정되지 않습니다. 더 긴 대본은 여러 Job으로 나누세요.

TTS 1.0 요금은 1,000자당 $0.0475이며, 기본적으로 5.5% 에이전트 수수료가 더해집니다. 공백과 문장 부호도 사용량에 포함됩니다. 지갑은 Sume 요금 체계에서 설명합니다.

출처

관련 글

개발자 카테고리의 다른 글

개발자 글 전체 보기

작성자 Sume