TTS 방송
개요
TTS 방송에는 두 가지 진입점이 있습니다. oe.broadcast.tts.send() 는 텍스트를 직접 입력해 합성 후 방송하고, oe.broadcast.tts.fromContent() 는 이미 합성된 컨텐츠를 즉시 방송합니다. 진행 중인 방송은 oe.broadcast.tts.stop() 으로 중단합니다.
시작하기 전에
TTS 방송을 시작하기 전에 아래를 준비하세요.
- 파트너 앱에
TTS_EXECUTEcapability grant 가 있어야 합니다. 없으면CapabilityDenied에러가 발생합니다. - 방송 대상이 될 디바이스 또는 그룹의 handle 이 필요합니다. 대상 선택 절에서 조회 방법을 안내합니다.
대상 선택
방송 대상은 targetHandle(디바이스 또는 그룹 handle) 또는 deviceHandles(디바이스 handle 다중)로 지정합니다. 대상 목록은 아래와 같이 조회합니다.
const [devices, groups] = await Promise.all([
oe.targets.list(),
oe.groups.list(),
]);
각 항목의 handle 값을 대상 지정에 사용합니다. 그룹에 방송하면 그룹에 속한 ASSIGNED 상태 디바이스 전체가 대상이 됩니다.
DeviceView.online 은 조회 시점의 온라인 여부를 나타내는 정보 라벨입니다. 방송 시점의 실제 상태를 보장하지 않으므로 대상 선택 제한 근거로 사용하지 마세요.
언어 선택지 — oe.languages.list()
번역 언어 선택지를 구성할 때 사용합니다. 앱이 바인딩된 현장의 활성 TTS 언어 목록을 반환합니다.
기본 정보
| 메서드 | 앱 capability | 참조 handle 조건 | 반환 타입 |
|---|---|---|---|
oe.languages.list() | 없음(게이트 없음) | 없음 | Promise<LanguageOption[]> |
요청
입력 파라미터가 없습니다.
응답
| 이름 | 타입 | 설명 |
|---|---|---|
code | string | 언어 코드(canonical lowercase) |
name | string | 언어 표시명 |
예제
const langs = await oe.languages.list();
for (const lang of langs) {
console.log(lang.code, lang.name);
}
참고: 목록은
ko(원문 언어)가 먼저 오고, 이후 표시명 가나다순으로 정렬됩니다.
직접 입력 방송 — oe.broadcast.tts.send()
원문 텍스트를 입력하면 번역·합성을 거쳐 대상에 방송합니다. 원문 언어는 항상 한국어입니다.
기본 정보
| 메서드 | 앱 capability | 참조 handle 조건 | 반환 타입 |
|---|---|---|---|
oe.broadcast.tts.send(input) | TTS_EXECUTE | targetHandle 또는 deviceHandles 가 유효한 노출 대상이어야 함(그룹은 ASSIGNED 디바이스 전체로 전개, 디바이스 다중은 handle 전원이 유효해야 함) | Promise<BroadcastReceipt> |
요청
| 이름 | 타입 | 설명 | 기본값 | 필수 |
|---|---|---|---|---|
targetHandle | string | 대상 디바이스 또는 그룹 handle | - | 조건부 |
deviceHandles | string[] | 대상 디바이스 handle 다중 지정 | - | 조건부 |
text | string | 방송할 텍스트(한국어 원문) | - | O |
language | string | 원문 언어 코드. 항상 "ko" | - | O |
additionalLanguages | string[] | 추가 번역 언어. 배열 순서가 재생 순서로 유지됨 | [] | X |
partial | boolean | 대상 일부 제외를 허용할지 여부 | - | X |
expectedRevision | number | 대상 그룹의 revision 고정. 어긋나면 TargetRevisionStale | - | X |
idempotencyKey | string | 중복 방송 방지 키 | - | O |
주의:
targetHandle과deviceHandles는 동시에 지정할 수 없습니다. 둘 중 정확히 하나만 지정하세요.
주의: 동일한
idempotencyKey로 다른 페이로드를 재전송하면IdempotencyKeyReused에러가 발생합니다. 네트워크 오류로 재시도할 때는 동일한 키를 재사용하고, 새로운 방송을 시작할 때는 새 키를 생성하세요.
응답
| 이름 | 타입 | 설명 |
|---|---|---|
sessionId | string | null | 방송 세션 ID. status 가 DENIED_BUSINESS 면 null |
status | "ADMITTED" | "DENIED_BUSINESS" | 방송 수락 여부 |
resolvedDeviceCount | number | 실제 방송 대상 디바이스 수 |
revision | number | 대상 그룹의 현재 revision |
denyReason | string | null | 거부 사유(DENIED_BUSINESS 시) |
skipped | string[] | 방송에서 제외된 디바이스 handle 목록 |
참고: 응답에는
admitted(파생 값) 필드도 포함되지만, 수락 여부 판정에는status를 사용하세요.
예제
const receipt = await oe.broadcast.tts.send({
targetHandle,
text: "출입을 통제합니다",
language: "ko",
additionalLanguages: ["en", "ja"],
idempotencyKey: crypto.randomUUID(),
});
const receipt2 = await oe.broadcast.tts.send({
deviceHandles,
text: "출입을 통제합니다",
language: "ko",
idempotencyKey: crypto.randomUUID(),
});
대표 에러
| 에러 | HTTP 상태 | 원인 | 해결 방법 |
|---|---|---|---|
CapabilityDenied | 403 | 앱에 TTS_EXECUTE capability grant 가 없음 | Open Echo 지원 채널로 grant 를 요청하세요 |
TargetUnresolved | 404 | targetHandle/deviceHandles 를 해석할 수 없음 | 대상 handle 이 유효한지 대상 목록을 다시 조회해 확인하세요 |
IdempotencyKeyReused | 409 | 동일한 idempotencyKey 로 다른 페이로드를 재전송함 | 새 방송마다 새 키를 생성하세요 |
DeviceInUse | 409 | 대상 중 사용 중인 디바이스가 포함됨 | 점유가 풀린 뒤 다시 시도하세요 |
UsageCapExceeded | 429 | 일일 TTS 방송 한도를 초과함 | 다음 날 자정(UTC) 이후 다시 시도하세요 |
컨텐츠 방송 — oe.broadcast.tts.fromContent()
이미 합성이 끝난 노출 컨텐츠를 대상에 즉시 방송합니다.
기본 정보
| 메서드 | 앱 capability | 참조 handle 조건 | 반환 타입 |
|---|---|---|---|
oe.broadcast.tts.fromContent(input) | TTS_EXECUTE + CONTENT | 대상 handle 조건은 tts.send() 와 동일. contentHandle 은 CONTENT EXECUTE grant 가 있어야 함 | Promise<BroadcastReceipt> |
요청
| 이름 | 타입 | 설명 | 기본값 | 필수 |
|---|---|---|---|---|
contentHandle | string | 방송할 노출 컨텐츠 handle | - | O |
targetHandle | string | 대상 디바이스 또는 그룹 handle | - | 조건부 |
deviceHandles | string[] | 대상 디바이스 handle 다중 지정 | - | 조건부 |
languageOrder | string[] | 재생 언어 순서. 생략하면 컨텐츠 자체 순서를 따름 | - | X |
partial | boolean | 대상 일부 제외를 허용할지 여부 | - | X |
expectedRevision | number | 대상 그룹의 revision 고정 | - | X |
idempotencyKey | string | 중복 방송 방지 키 | - | O |
주의:
targetHandle과deviceHandles는 동시에 지정할 수 없습니다. 둘 중 정확히 하나만 지정하세요.
주의: 동일한
idempotencyKey로 다른 페이로드를 재전송하면IdempotencyKeyReused에러가 발생합니다. 네트워크 오류로 재시도할 때는 동일한 키를 재사용하고, 새로운 방송을 시작할 때는 새 키를 생성하세요.
응답
| 이름 | 타입 | 설명 |
|---|---|---|
sessionId | string | null | 방송 세션 ID. status 가 DENIED_BUSINESS 면 null |
status | "ADMITTED" | "DENIED_BUSINESS" | 방송 수락 여부 |
resolvedDeviceCount | number | 실제 방송 대상 디바이스 수 |
revision | number | 대상 그룹의 현재 revision |
denyReason | string | null | 거부 사유(DENIED_BUSINESS 시) |
skipped | string[] | 방송에서 제외된 디바이스 handle 목록 |
예제
const receipt = await oe.broadcast.tts.fromContent({
contentHandle,
targetHandle,
languageOrder: ["ko", "en"],
idempotencyKey: crypto.randomUUID(),
});
const receipt2 = await oe.broadcast.tts.fromContent({
contentHandle,
deviceHandles,
idempotencyKey: crypto.randomUUID(),
});
대표 에러
| 에러 | HTTP 상태 | 원인 | 해결 방법 |
|---|---|---|---|
ContentNotReady | 400 | 컨텐츠 오디오가 아직 준비되지 않음 | 합성이 끝난 뒤 다시 시도하세요 |
CapabilityDenied | 403 | TTS_EXECUTE 또는 CONTENT grant 가 없음 | Open Echo 지원 채널로 grant 를 요청하세요 |
TargetUnresolved | 404 | 대상 또는 contentHandle 을 해석할 수 없음 | handle 유효성을 다시 확인하세요 |
IdempotencyKeyReused | 409 | 동일한 idempotencyKey 로 다른 페이로드를 재전송함 | 새 방송마다 새 키를 생성하세요 |
방송 중단 — oe.broadcast.tts.stop()
재생 중(PLAYING)인 자기 소유 세션을 중단합니다.
기본 정보
| 메서드 | 앱 capability | 참조 handle 조건 | 반환 타입 |
|---|---|---|---|
oe.broadcast.tts.stop(sessionId) | 없음(소유권만) | sessionId 가 호출 앱 소유의 세션이어야 함 | Promise<void> |
요청
| 이름 | 타입 | 설명 | 기본값 | 필수 |
|---|---|---|---|---|
sessionId | string | 중단할 방송 세션 ID | - | O |
응답
반환값이 없습니다(Promise<void>).
참고:
stop()이 resolve 되어도 방송이 즉시 끝난 것은 아닙니다. 종결 확인은subscribe()로 받는phase === "STOPPED"(terminal) 이벤트로 합니다.
예제
await oe.broadcast.tts.stop(sessionId);
대표 에러
| 에러 | HTTP 상태 | 원인 | 해결 방법 |
|---|---|---|---|
BroadcastIneligible | 409 | 세션이 재생 중이 아니거나(합성 중·이미 종결) 중단할 수 없는 상태 | 세션 상태를 구독으로 확인한 뒤 다시 시도하세요 |
방송 상태 확인
send() 와 fromContent() 모두 BroadcastReceipt 를 반환합니다. status 로 수락 여부를 확인하고, sessionId 로 진행 상태를 구독합니다.
| status | 의미 |
|---|---|
ADMITTED | 방송이 수락되어 진행 중입니다. sessionId 로 구독하세요 |
DENIED_BUSINESS | 정책상 거부되었습니다. sessionId 는 null, denyReason 에 사유가 담깁니다 |
const receipt = await oe.broadcast.tts.send({
targetHandle,
text: "출입을 통제합니다",
language: "ko",
idempotencyKey: crypto.randomUUID(),
});
if (!receipt.sessionId) {
console.warn("방송 거부:", receipt.denyReason);
} else {
oe.broadcast.subscribe(receipt.sessionId, {
onPhase: (snap) => {
console.log(snap.phase, snap.terminal);
},
});
}
실시간 진행 상태 추적은 진행 구독을 참조하세요.
참고 코드
레퍼런스 앱의 TTS 방송 화면 구현은 partner-app/src/features/tts/TtsBroadcast.tsx 에서 확인할 수 있습니다.

