OPWS Developers

Control-Plane 개요

개요

Open Echo Partner SDK의 control-plane은 방송 인프라(그룹·컨텐츠·템플릿·예약) 관리와 이력 조회를 담당합니다. 방송 실행(broadcast.*)과 달리 설정 데이터를 읽고 쓰는 API입니다.

이 문서는 control-plane 뿐 아니라 SDK 전체에 적용되는 capability·grant 권한 모델을 규범적으로 설명합니다. 다른 가이드 문서의 "앱 capability"·"참조 handle 조건" 서술은 이 문서의 정의를 전제합니다.

권한 모델

capability 종류

파트너 앱의 모든 API 호출은 capability(기능 단위 권한) 검사를 먼저 거칩니다. 앱에 해당 capability grant 가 없으면 CapabilityDenied(403)가 발생합니다. capability는 8종입니다.

capability게이트하는 기능
TTS_EXECUTETTS 방송 실행
MIC_EXECUTEMIC 방송 실행
DEVICE노출 디바이스 목록 조회
DEVICE_GROUP디바이스 그룹 CRUD
CONTENT방송 컨텐츠 조회·생성·수정
TEMPLATE방송 템플릿 CRUD
SCHEDULE예약 방송 CRUD
HISTORY방송 이력 조회, 진행 구독

grant 액션 — MANAGE · READ · EXECUTE

DEVICE_GROUP·CONTENT·TEMPLATE·SCHEDULE·HISTORY·DEVICE capability는 세부 액션과 함께 부여됩니다.

  • MANAGE — 리소스의 생성·수정·삭제와 소유권 기반 단건 조회를 포함한 관리. groups(create/get/addMembers/removeMembers/delete/memberstemplates(create/get/update/deleteschedules(create/get/update/delete)가 MANAGE 로 게이트됩니다.
  • READ — 목록 조회, 그리고 노출·grant 범위 기반 조회. groups.list/templates.list/schedules.list, contents.list/get, history.list/get, targets.list가 READ 로 게이트됩니다. (contents.get은 소유권이 아니라 handle 단위 READ grant로 게이트된다는 점에서, 소유권을 검증하는 groups.get/templates.get/schedules.get(MANAGE)과 메커니즘이 다릅니다.)
  • EXECUTE — 특정 handle(컨텐츠·템플릿)을 방송에 실행으로 사용할 권한. handle 별로 별도 부여되는 grant 입니다.

handle 단위 EXECUTE grant

일부 호출은 capability 게이트를 통과해도, 요청에 담은 특정 handle 을 실행하려면 그 handle 에 대한 EXECUTE grant 가 추가로 필요합니다.

호출EXECUTE grant 가 필요한 handle
oe.broadcast.tts.fromContent()contentHandleCONTENT EXECUTE
oe.templates.create()contentHandleCONTENT EXECUTE
oe.schedules.create()templateHandleTEMPLATE EXECUTE
oe.schedules.update()(템플릿 교체 시)templateHandleTEMPLATE EXECUTE

주의: MANAGE grant 는 EXECUTE grant 를 자동으로 포함하지 않습니다. 앱이 직접 만든 컨텐츠·템플릿이라도 방송에 실행하려면 별도의 EXECUTE grant 가 필요할 수 있습니다. EXECUTE grant 가 없으면 TargetUnresolved(404)로 응답합니다 — handle 이 존재하는지 자체를 노출하지 않습니다.

노출(exposure)과 위임(delegation)

파트너 앱이 참조할 수 있는 디바이스 handle 은 두 가지 방식으로 노출됩니다.

  • 선별 노출 — 디바이스별로 개별 grant 를 받아 oe.targets.list()에 나타납니다.
  • 전체 위임 — 현장 단위로 위임을 받으면, 개별 grant 없이 그 현장에서 사용 가능한 디바이스 전체가 자동으로 oe.targets.list()에 나타납니다.

어느 방식으로 노출됐든 handle 은 방송 대상 지정(broadcast.*)·그룹 멤버 구성·템플릿 개별 디바이스 지정(deviceHandles)에 동일하게 사용할 수 있습니다. oe.targets.list()가 반환하는 목록은 조회 시점의 스냅샷입니다 — 그 사이 노출이 사라진 handle 을 나중에 사용하면 TargetUnresolved가 발생할 수 있습니다.

all-or-none 시맨틱

여러 handle 을 한 번에 지정하는 요청(deviceHandles 다중 지정, 그룹 멤버 추가·제거 등)은 all-or-none 으로 검증됩니다. 하나라도 해석할 수 없으면 요청 전체가 거부되며, 일부만 반영되는 경우는 없습니다.

참고: 방송 실행 시점의 partial 옵션(대상 일부 제외 허용)은 이 handle 해석 단계와는 다른 층입니다. all-or-none 은 handle 이 유효한 참조인지를 검증하는 단계이고, partial 은 유효한 대상 중 방송 시점에 사용할 수 없는 대상을 제외할지를 결정하는 단계입니다. 상세는 TTS 방송을 참조하세요.

소유권과 존재 은닉

그룹·템플릿·예약·자작 컨텐츠는 생성한 파트너 앱이 소유합니다. 다른 앱이 소유한 handle 을 조회·수정·삭제하려고 하면, 권한 부족을 알리는 대신 TargetUnresolved(404) 로 응답합니다 — 그 handle 이 존재하는지 자체를 노출하지 않기 위한 설계입니다.

노출 디바이스 조회 — oe.targets.list()

앱에 노출된 디바이스 목록을 조회합니다. 방송 대상 지정(broadcast.*)·그룹 멤버 구성·템플릿 개별 디바이스 지정의 소스입니다. 선별 노출(개별 grant)과 전체 위임(현장 단위) 두 방식 모두 이 목록에 함께 나타납니다(위 "노출과 위임" 절 참조).

기본 정보

메서드앱 capability참조 handle 조건반환 타입
oe.targets.list()DEVICE없음 — capability 만으로 게이트되고, 이후 노출된(선별 노출 + 전체 위임) 디바이스 집합이 반환됨Promise<DeviceView[]>

요청

입력 파라미터가 없습니다.

응답

이름타입설명
handlestring디바이스 handle
namestring디바이스 이름
statusstring디바이스 상태
onlineboolean | undefined조회 시점의 온라인 여부를 나타내는 정보 라벨. 방송 시점의 실제 상태를 보장하지 않으므로 대상 선택 제한 근거로 사용하지 마세요

예제

ts
const devices = await oe.targets.list();
for (const d of devices) {
  console.log(d.handle, d.name, d.status);
}

대표 에러

에러HTTP 상태원인해결 방법
CapabilityDenied403앱에 DEVICE capability grant 가 없음Open Echo 지원 채널로 grant 를 요청하세요

Capability 요약

API필요 capability
oe.targets.list()DEVICE READ
oe.groups.list()DEVICE_GROUP READ
oe.groups.create(), .get(), .addMembers(), .removeMembers(), .members(), .delete()DEVICE_GROUP MANAGE
oe.templates.list()TEMPLATE READ
oe.templates.create(), .get(), .update(), .delete()TEMPLATE MANAGE(create()는 추가로 CONTENT EXECUTE(contentHandle) + DEVICE READ×N 또는 DEVICE_GROUP READ(대상 handle))
oe.schedules.list()SCHEDULE READ
oe.schedules.create(), .get(), .update(), .delete()SCHEDULE MANAGE(create()/템플릿 교체 시 update()templateHandle은 추가로 TEMPLATE EXECUTE)
oe.contents.list(), oe.contents.get()CONTENT READ
oe.contents.create(), oe.contents.update()CONTENT MANAGE
oe.history.list(), oe.history.get(), oe.broadcast.subscribe()HISTORY READ
oe.broadcast.tts.send()TTS_EXECUTE
oe.broadcast.mic.start()MIC_EXECUTE
oe.broadcast.tts.fromContent()TTS_EXECUTE + CONTENT EXECUTE(contentHandle)
oe.broadcast.tts.stop(), MicSession.stop()없음(세션 소유권만)
oe.languages.list()없음(게이트 없음)

capability가 없으면 각 API 호출에서 CapabilityDenied(403)가 발생합니다. Open Echo 지원 채널로 capability 부여를 요청하세요 (킷 루트 ONBOARDING.md).

SDK 비대칭 요약

파트너 SDK의 control-plane API는 운영자 콘솔과 기능이 동일하지 않습니다. 아래 표에서 제공하지 않는 작업을 확인하세요.

리소스listgetcreateupdatedelete기타
groups없음members, addMembers, removeMembers
templates
schedules템플릿 기반만
contents없음update는 add-only(언어 추가)
historyread-only (단 get으로 상세 조회 가능 — 재방송 조합용)

비대칭 주요 사항:

  • groups.update 없음 — 그룹명 변경 미지원. 멤버십 편집(addMembers / removeMembers)만 가능.
  • contents.delete 없음 — 생성(create: 제목+한국어 원문+번역 언어)·수정(update: 제목 변경+언어 add-only)은 가능. 즉석 1회 방송은 broadcast.tts.send(), 노출 컨텐츠 즉시 방송은 broadcast.tts.fromContent().
  • schedules템플릿 기반만CreateScheduleInput.templateHandle 필수. 컨텐츠+대상 직접 예약 미노출.

목록 필터

네 개 목록 API는 서버측 필터 파라미터를 받습니다. 모든 텍스트 필터는 부분일치·대소문자 무시입니다.

리소스메서드필터대상 필드
groupslist({ search })search그룹명(label)
contentslist({ search })search제목(title)
scheduleslist({ search })search예약명(name)
historylist({ type, search })type · searchtype=방송 유형('MIC' | 'TTS') · search=컨텐츠 제목

templates.list()는 필터를 받지 않습니다(무인자). historytype은 공개 계약값('MIC' / 'TTS')을 전달합니다 — 상세는 방송 이력HistoryView.type 설명을 참조하세요.

권장 사용 순서

방송 예약을 위해서는 다음 순서로 리소스를 준비합니다.

컨텐츠 확보 (contents.list / contents.create)
      ↓
그룹 생성 + 멤버 편집 (groups.create → addMembers)
      ↓
템플릿 생성 (templates.create — contentHandle + groupHandle)
      ↓
예약 생성 (schedules.create — templateHandle 필수)

자주 만나는 상황

권한 없음 → CapabilityDenied

ts
import { CapabilityDenied } from "@openecho/partner-sdk";

try {
  await oe.schedules.list();
} catch (err) {
  if (err instanceof CapabilityDenied) {
    // SCHEDULE capability가 없음 — Open Echo 지원 채널로 grant 부여 요청
  }
}

대상 없음 → TargetUnresolved

템플릿에 지정된 그룹에 ASSIGNED 상태 디바이스가 없으면 방송 시 TargetUnresolved 에러가 발생합니다. 그룹에 활성 디바이스(ASSIGNED)를 추가하세요.

빈 목록

list() 결과가 빈 배열([])인 것은 정상입니다. 아직 생성된 리소스가 없음을 의미합니다.

ts
const groups = await oe.groups.list();
if (groups.length === 0) {
  // 그룹이 아직 없음 — 생성 필요
}

게이트웨이 마스킹 주의

게이트웨이/프록시가 API 4xx 응답을 HTML(200)로 치환하면, SDK는 GatewayMasked(HTML 본문)·InvalidApiResponse(비-JSON 본문) 에러를 직접 throw 합니다. 목록 API 를 여러 개 동시에 호출하는 화면에서는 Promise.allSettled로 한 자원의 실패가 나머지를 무너뜨리지 않게 하는 것을 권장합니다. 진단 절차는 에러 처리를 참조하세요.

참고 코드

레퍼런스 앱의 그룹·컨텐츠·템플릿·예약·이력 화면 구현은 각 기능별 가이드의 "참고 코드" 절에서 파일 단위로 안내합니다.

각 리소스 상세