
Zoom Translator API 핸즈온 — Scribe의 전사 내용을 다국어화하기 (Fast & Batch)
요약
Zoom AI Services의 Translator API를 활용하여 Scribe 전사 데이터를 다국어로 번역하는 Node.js 핸즈온 가이드입니다. Fast(동기) 및 Batch(비동기) 모드의 차이점과 실제 구현 시 주의해야 할 제약 사항을 다룹니다.
핵심 포인트
- Fast와 Batch 두 가지 모드로 API 호출 가능
- Fast 모드는 4,000자 제한으로 직접 청크 분할 필요
- Batch 모드는 S3 연동 및 비동기 처리를 지원
- 1회 요청당 하나의 타겟 언어만 지정 가능
지난 기사에서는 Zoom AI Services의 **Scribe API (Batch)**를 사용하여 일본어 음성 파일을 일괄 전사(Transcription)하는 핸즈온을 작성했습니다.
2026년 5월, Zoom AI Services에 새로운 기능이 추가되었습니다.
- 🌏 Translator API (Fast + Batch) 정식 출시 - 🇯🇵 일본어를 포함한 13개 언어 지원
- 📦 Scribe와 마찬가지로, Fast (동기·텍스트 직접 전달)와 Batch (비동기·S3 연동)의 2가지 모드 제공
본 기사에서는 Translator API의 Fast / Batch 양쪽을 실제로 호출해 보며, "레퍼런스에는 작동한다고 되어 있지만 실제로는 작동하지 않는 패턴"이나 "VTT·JSON을 통째로 넣으면 어떻게 되는가" 등의 실제 동작을 Node.js 핸즈온 형식으로 기록합니다. 지난 Scribe 핸즈온에서 만든 코드 자산(auth.js, batch.js, index.js)에 추가하는 방식으로 진행합니다.
본 기사는 2026년 5월 시점의 동작을 바탕으로 합니다. Translator API는 출시 직후이기 때문에 레퍼런스와 실제 동작 사이에 차이가 보이는 부분이 있습니다. 기재된 UNSUPPORTED_MEDIA 등의 에러는 향후 해결될 가능성이 있습니다.
이번 처리 플로우는 다음과 같은 형태입니다. Scribe로 생성한 전사 결과를 Translator에 통과시켜 다국어화하는 파이프라인을 상정하고 있습니다.
이번 기사에서는 Batch 작업의 처리 상태를 폴링(Polling)으로 가져오고 있지만, 공식 문서에 명시된 대로 Webhook을 통해 푸시 알림을 받는 것도 가능합니다.
| 항목 | Fast | Batch |
|---|---|---|
| 엔드포인트 | POST /v2/aiservices/translator/translate | POST /v2/aiservices/translator/jobs |
| 처리 방식 | 동기 (즉시 응답) | 비동기 (폴링 또는 Webhook) |
| 입력 | text 필드에 직접 (인라인) | S3 상의 파일 |
| 입력 포맷 | 플레인 텍스트 (실질적으로 무엇이든 문자열 취급) | (2026년 5월 시점) .txt만 가능 |
| 입력 크기 상한 | 4,000자 / 요청 | 4,000자 / 16 KB / 파일 |
| 작업(Job) 상한 | - | 1,000 파일 / MANIFEST, 1억 자 / 작업, 10억 자 / 일 |
| 출력 | JSON 응답 (인라인) | S3에 {original}_{file_id}.json으로 쓰기 |
| AWS 인증 | 불필요 | 필수 (STS 임시 자격 증명) |
| 언어 지정 | 요청마다 | 작업 단위 |
source_language | 필수 (auto-detect 없음) | 필수 |
ISO 로케일(Locale) 형식으로 지정합니다.
| 코드 | 언어 |
|---|---|
en-US | 영어 |
ja-JP | 일본어 |
zh-CN / zh-Hans | 중국어 (간체) |
zh-TW / zh-Hant | 중국어 (번체) |
ko-KR | 한국어 |
es-ES | 스페인어 |
fr-FR | 프랑스어 |
de-DE | 독일어 |
pt-PT / pt-BR | 포르투갈어 (유럽/브라질) |
it-IT | 이탈리아어 |
target_languages는 배열이지만, 1회 요청당 1개 언어까지라는 제한이 있습니다. 여러 언어로 번역하고 싶다면 target을 변경하여 API를 여러 번 호출해야 합니다.
또한, Fast Mode의 공식 문서에는 **"1회 요청당 1개 언어로의 번역", "source_language는 명시 필수", "최대 입력 4,000자"**가 명시되어 있습니다. 긴 문장을 번역할 경우에는 직접 청크(Chunk) 분할을 해야 합니다.
Fast 모드에서 JSON이나 VTT를 그대로 요청에 던지는 것은 가능하지만, 문장 이외의 파라미터나 제어 문자 등이 기대한 대로 그대로 출력될지에 대해서는 보장할 수 없습니다. 안전을 위해, 요청 전에 텍스트 파일 또는 텍스트만 있는 인라인 형식으로 변환한 후 실행하는 것을 권장합니다.
JWT 인증은 이전의 Scribe API와 동일한 메커니즘입니다. auth.js의 generateJwt()를 그대로 재사용합니다.
이전의 디렉토리 구조를 그대로 활용합니다.
ai-services-handson/
├── .env
├── auth.js # JWT 인증 (공통)
...
// fast.js
import { generateJwt } from "./auth.js";
const TRANSLATOR_FAST_URL =
...
포인트:
text에는 UTF-8 문자열을 직접 전달합니다.source_language에 대해, 레퍼런스에는 명시되어 있지 않지만 필수로 설정해야 합니다.- 반환값은
console.log하지 않고, 호출 측에서 다룰 수 있도록 객체를 반환하는 설계로 합니다.
Scribe Batch와 요청 구조가 거의 동일합니다. 차이점은 config 내부 내용뿐입니다.
// batch.js (submit 부분만 발췌)
import { generateJwt } from "./auth.js";
const BASE_URL = "https://api.zoom.us/v2/aiservices/translator";
...
include_globs의 기본값을 ["/*.txt"]**로 설정한 것이 이번의 중요한 포인트입니다. AI Services 전체의 스키마 설명에서는 **/*.vtt 예시도 등장하지만, Translator Batch 전용 가이드의 cURL 예시에서는 .txt만 지원하고 있으며, 실제 동작도 .txt만 제시됩니다 (2026년 5월 기준). VTT를 전달하면 UNSUPPORTED_MEDIA로 인해 skip 됩니다. 자세한 내용은 §5의 트러블슈팅을 참조하십시오. 출력 파일명 규칙은 {original_filename}_{file_id}.json으로 고정되어 있으며, 입력 확장자와 관계없이 항상 .json으로 출력됩니다. 예: meeting_001.txt를 입력하면 meeting_001.txt_<uuid>.json이 출력되는 방식입니다.
상태 확인, 파일 목록, 폴링(Polling)은 Scribe Batch와 거의 동일한 구조이므로 생략합니다. 이전 기사를 참조해 주세요.
commander로 3개의 서브 커맨드(Subcommand)를 추가합니다.
// index.js (추가분만 발췌)
import { readFile } from "node:fs/promises";
import { translateFast } from "./fast.js";
...
f.error.code와 f.error.message를 반드시 화면에 출력하도록 하십시오. Translator Batch는 state가 QUEUED 상태로 머물며 전이되지 않는 버그가 있어, 개별 파일의 에러를 확인하지 않으면 "무슨 일이 일어났는지 알 수 없는" 상태에 빠지게 됩니다 (§5.4 참조).
node index.js translate-fast \
--text "こんにちは、今日もよろしくお願いします。" \
--source ja-JP --target en-US
=== Translator Fast API ===
Source: ja-JP
Target: en-US
...
심플합니다. 1초도 걸리지 않고 반환됩니다.
시험 삼아 원본이 VTT인 파일을 Translator Fast에 그대로 전달해 보겠습니다.
node index.js translate-fast ./samples/transcript.vtt --source ja-JP --target en-US
--- en-US ---
WEBVTT
00:00:00.000 --> 00:00:03.500
...
일단 깔끔하게 번역되었습니다. WEBVTT 헤더와 타임스탬프에는 전혀 손을 대지 않고 본문만 번역해 줍니다. 이는 매우 편리합니다. Translator 모델이 "타임스탬프는 번역 대상이 아니다"라는 것을 이해하고 있는 듯합니다.
실용성이 높아 보이지만, 안전을 위해 VTT > TXT 변환을 거친 후 요청하는 것을 권장합니다.
더 나아가, 실험 삼아 Scribe API의 JSON 출력을 그대로 text 필드에 채워서 던지면 어떻게 될까요?
node index.js translate-fast ./samples/transcript.json --source ja-JP --target en-US
--- en-US ---
{
"model": "zoom-asr-ja-v1",
...
아깝습니다! 구조는 대체로 유지해주지만, 다음과 같은 문제가 발생합니다:
duration_sec→duration_SEC("sec"를 약어로 판단하여 대문자로 변경)text_display→text display(언더스코어를 공백으로 변경)- 마지막
}가{로 바뀌어 있어 JSON 파싱 불가 (JSON parsing error)
LLM (Large Language Model) 기반이기 때문에 "과한 수정"과 "토큰 생성 실수"가 혼재되어 나타납니다.
Scribe의 JSON 출력을 그대로 Translator에 전달하는 파이프라인은 정상적으로 작동하지 않습니다. JSON 형식이 깨질 가능성이 높기 때문에 기계적인 후속 처리에 태울 수 없습니다. Scribe → Translator를 제대로 연결하려면, JSON에서 segments[].text를 추출하여 각 세그먼트별로 Fast Translator에 던지도록 구성해야 합니다.
S3 버킷에 txt_input/ 폴더를 만들고, japanese.txt와 english.txt를 업로드합니다.
samples/japanese.txt:
こんにちは、今日もよろしくお願いします。
今日はAIの活用について話していきましょう。
...
실행:
node index.js translate-batch --source ja-JP --target en-US --ref txt-trial-001
=== Translator Batch API ===
Input: s3://zoom-scribe-handson-<yourname>/txt_input/
Output: s3://zoom-scribe-handson-<yourname>/translations/
...
S3의 translations/ 하위에 번역 결과가 JSON으로 기록됩니다.
Translator API는 새로운 API이므로, 몇 가지 확인이 필요한 포인트가 있습니다.
{
"state": "FILE_SKIPPED",
"error": {
...
원인: Translator Batch는 2026년 5월 시점에서 .txt 파일만 지원합니다. AI Services 전체 스키마에는 **/*.vtt glob 예시가 포함되어 있지만, Translator Batch 전용 가이드의 cURL 예시는 *.txt만 포함하고 있습니다. 실제 동작도 .txt로 한정되며 그 외의 형식은 거부됩니다. .json 역시 마찬가지입니다. 참고로, Summarizer Batch는 .srt / .txt / .vtt 세 종류를 지원합니다 (동일한 AI Services라도 API마다 지원 확장자가 다릅니다). Translator도 향후 지원 확장자가 확대될 가능성은 높습니다.
대처:
입력 형식을 .txt로 통일하십시오.
include_globs에 .vtt를 포함하면 작업(Job)은 투입되지만, 각 파일이 FILE_SKIPPED 상태가 됩니다. 작업 전체의 state는 QUEUED 상태에서 전환되지 않는 경우가 많으므로, listJobFiles를 호출하여 개별 에러를 확인하기 전까지는 원인을 알 수 없습니다 (§5.4 참조).
{
"state": "FILE_FAILED",
"error": {
...
원인: Zoom이 S3에서 파일을 가져오는 단계에서 실패하고 있습니다. 흔히 발생하는 원인은 다음 두 가지 중 하나입니다:
- (가장 빈번함)
AWS_SESSION_TOKEN이 만료됨 - IAM 정책에서 해당 prefix에 대한
s3:GetObject권한이 없음
ListBucket은 통과하여 (total_files까지는 가져올 수 있음) 개별 GetObject에서 실패하는 형태로 나타납니다. AWS 문맥에서 설명하자면:
| 구분 | 원인 | 기대되는 동작 |
|---|---|---|
| AuthN (인증) 실패 | SessionToken 만료 / 무효 | submit 단계에서 400 ERROR_CLIENT_AWS_CREDENTIAL로 차단될 수도 있지만, submit은 클라이언트 SDK 검증만 통과하고 GetObject 단계에서 처음 발견되는 케이스도 있음 |
| AuthZ (인가) 실패 | 토큰은 유효하나, prefix 권한 부족 | submit 성공 → ListBucket 성공 → GetObject에서 AccessDenied |
Common_LinkException은 위 두 가지를 모두 흡수하는 Zoom 측의 범용 에러이므로, 먼저 SessionToken 재발급으로 원인을 분리한 뒤, 그 다음에 IAM 정책을 확인하는 것이 가장 빠른 경로입니다.
대응:
가장 비용이 적게 드는 분리 방법으로, STS를 재발급합니다:
AWS_ACCESS_KEY_ID=AKIA... \
AWS_SECRET_ACCESS_KEY=... \
AWS_SESSION_TOKEN= \
...
→ .env의 세 가지 값을 업데이트합니다.
그래도 안 된다면 IAM 정책의 Resource를 확인하세요. 버킷의 특정 폴더만 허용되어 있고 그 외 영역이 커버되지 않은 케이스를 체크합니다.
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
...
동작 확인용이라면 버킷 전체를 허용하는 것이 편합니다.
§4.1.3에서 관찰한 것처럼, duration_sec가 duration_SEC로 바뀌거나, 끝부분의 }가 {로 변하는 현상이 발생합니다.
원인: Translator는 LLM 기반이므로, 입력 텍스트 전체를 의미적으로 해석한 뒤 번역하기 때문에 JSON의 키(Key) 이름이나 기호도 번역 대상이 될 수 있습니다.
대응: Scribe 출력 JSON을 Translator로 흘려보내는 파이프라인을 구축할 경우, JSON에서 segments[].text와 같이 본문만 추출하여 1개 세그먼트씩 Fast Translator로 던지는 구성으로 만듭니다.
{
"state": "QUEUED",
"summary": {
...
total_files == skipped 상태로 실질적으로는 모든 파일이 종료 상태임에도 불구하고, 작업(Job) 전체의 state가 QUEUED 상태로 머물며 움직이지 않습니다.
원인: Translator Batch의 상태 전이(State Transition) 로직 문제로 인해, FILE_SKIPPED로만 채워진 작업을 종료 상태로 판정하지 못하는 것으로 보입니다. (2026년 5월 기준)
대응:
pollUntilComplete의 타임아웃을 짧게 설정합니다 (timeoutMs: 60_000등).- 병행하여
listJobFiles를 호출하고,summary.total_files == 각 카운트의 합계가 되면 종료된 것으로 직접 판정합니다.
// 직접 종료를 판정하는 워크아라운드 (Workaround)
function isTerminal(summary) {
const { total_files, queued, processing } = summary;
...
참고로 Webhook으로 알림을 받는 경우에도 이 패턴에서는 알림이 오지 않습니다. (2026년 5월 기준)
대략적으로 정리했습니다.
| 관점 | Fast Translator | Batch Translator |
|---|---|---|
| 처리 방식 | 동기 (즉시 응답) | 비동기 (Polling 또는 Webhook) |
| 입력 | 텍스트 직접 입력 | S3 상의 파일 |
| 입력 포맷 | Plain Text / VTT / JSON (실질적으로 모든 문자열) | (2026년 5월 기준) .txt만 가능 |
| AWS 인증 | 불필요 | 필수 (STS 임시 자격 증명) |
| 적합한 용도 | 실시간 번역, VTT 자막 다국어화, 소규모 작업 | 아카이브 일괄 번역, Scribe(.txt) 파이프라인 |
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기