RTMP
Cloudflare Stream 기반 B2B 멀티테넌트 라이브 방송 SaaS. 조직 단위로 격리된 라이브 채널을 만들어 iOS 앱에서 RTMP(S)/SRT로 송출하고, 워터마크·녹화·웹훅·API 키를 관리한다. 백엔드는 Workers 위의 Hono + Drizzle + Postgres(Hyperdrive), 프런트는 테넌트용 admin과 운영자용 console 두 개의 React SPA로 나뉜다. 셀프서브 가입, 플랜별 쿼터, Stripe 구독 결제까지 포함한다.
Cloudflare Stream 기반 B2B 멀티테넌트 라이브 채널 SaaS
왜 만들었는가
Cloudflare Stream은 글로벌 엣지와 다중 프로토콜 송출을 제공하는 좋은 인프라지만, 채널 하나를 열려면 Cloudflare 콘솔이나 API를 직접 다뤄야 한다.
문제는 그 접근 권한을 셀러·강사·현장 방송자에게 줄 수 없다는 것이다. Cloudflare 계정 자격증명은 스트리밍 기능만 떼어 부분 위임할 수 있는 물건이 아니라서, 콘솔을 열어주는 순간 계정 전체가 열린다.
그렇다고 요청이 올 때마다 운영자가 대신 발급해 주면 사람이 병목이 된다.
그래서 채널 발급 자체를 제품으로 만들었다. 관리 화면에서 채널을 만들면 뒤에서 Live Input이 자동 프로비저닝되고, 방송자에게는 송출에 필요한 값만 나간다. Cloudflare는 끝까지 보이지 않는 곳에 남는다.
운영하면서 드러난 제약도 설계에 반영됐다.
목록·상세 조회를 Cloudflare API로 처리하면 5분당 1,200요청 한도에 걸리기 때문에, 조회 경로는 자체 DB로 돌리고 Cloudflare 호출은 생성·수정·삭제 시점에만 한다.
또 Cloudflare와 DB는 별개 시스템이라, 생성이 Cloudflare에서 성공하고 DB 저장이 실패하면 보상 트랜잭션으로 Cloudflare 리소스를 되돌린다.
출발점은 단일 테넌트 구조였다. 고객사가 늘면서 데이터 격리도, 셀프서브 온보딩도, 과금도 없다는 것이 한계로 드러났고, org_id 기반 pooled 멀티테넌시로 전환하면서 백엔드를 Cloudflare Workers로 옮겼다.
프로젝트 설명
플랫폼의 축은 역할 분리다.
관리자가 발급한다.
방송자가 송출한다.
시청자가 본다.
관리자는 콘솔에서 채널을 만들고, 방송자는 서버 주소와 스트림 키 두 줄만 받아 OBS·vMix 같은 표준 인코더나 전용 iOS 앱으로 송출한다.
시청자는 매니페스트 URL 하나로 본다.
어느 단계에서도 Cloudflare 콘솔은 등장하지 않는다.
인증 구조
인증 realm은 둘로 나뉜다.
고객사 사용자
조직 역할:
owner > admin > member > viewer
플랫폼 운영진
별도 admin RBAC:
superadmin > operator > viewer
두 인증 영역은 서로 다른 테이블과 다른 라우트 프리픽스를 사용한다.
구성
플랫폼은 네 개의 구성 요소로 나뉜다.
rtmp-api-workers
백엔드 API.
단일 Worker가 두 청중을 라우트 프리픽스로 나눠 서빙한다.
/api/v1/*— 조직 스코프 고객 API/console/*— 플랫폼 운영 API
모든 테넌트 리소스는 앱 레이어에서 org_id로 강제 격리된다.
rtmp-admin
고객 콘솔.
고객사가 자기 조직 안에서 다음 항목을 관리한다.
라이브 채널
멤버
구독
워터마크
웹훅
API 키
루트에는 제품 소개 페이지가 서빙되고 로그인은 그 뒤에 있다.
rtmp-console
운영 콘솔.
플랫폼 운영진이 조직 경계를 넘어 모든 테넌트를 관리하는 내부 화면이다.
고객 콘솔과 배포가 분리돼 있어 고객이 내려받는 번들에는 운영 코드가 들어가지 않는다.
rtmp-ios
송출 앱.
PC와 OBS 없이 휴대폰만으로 송출하는 네이티브 앱이다.
세로 9:16 프리셋과 전·후면 멀티캠 PIP를 갖춰 라이브 커머스·숏폼에 맞춰져 있다.
주요 기능
채널 발급과 상태 동기화
채널 하나에 다음 항목이 함께 발급된다.
RTMPS 송출 엔드포인트
SRT 송출 엔드포인트
HLS 시청 URL
DASH 시청 URL
상태는 다음 5단계로 관리한다.
createdliveidledisablederrored
Cloudflare와 양방향 동기화하고, 삭제는 deleted_at 기반 소프트 삭제로 이력을 남긴다.
조직과 온보딩
다음 기능을 지원한다.
셀프서브 가입
이메일 인증
멤버 초대
한 사용자가 여러 조직에 속하고 조직 컨텍스트를 전환할 수 있다.
조직별 요청 레이트 리밋으로 테넌트 간 간섭을 차단한다.
플랜과 구독
플랜별 한도와 실사용량을 대조하는 쿼터를 적용한다.
Stripe를 통해 구독 결제를 처리하고, 결제 웹훅은 서명을 검증한다.
Stripe 키를 설정하지 않으면 결제 엔드포인트는 비활성 상태로 남는다.
API 키와 웹훅
외부 서버용 API 키를 스코프 단위로 읽기·쓰기를 분리해 발급한다.
아웃바운드 웹훅은 서명되며 전송 이력이 기록된다.
cron 스윕이 실패한 전송을 재시도한다.
거버넌스
감사 로그는 변경뿐 아니라 운영진의 크로스테넌트 조회까지 기록한다.
고객은 자기 조직의 감사 기록을 직접 열람할 수 있다.
워터마크는 조직 소유로 스코프되며 녹화본과 라이브 이벤트도 관리된다.
토큰 취급
Access JWT는 브라우저 메모리에만 둔다.
Refresh 토큰은 httpOnly·Secure 쿠키에 담는다.
재사용이 탐지되면 해당 주체의 토큰을 일괄 폐기한다.
Cloudflare 자격증명은 서버 밖으로 나가지 않는다.
기술 스택
백엔드와 두 콘솔 모두 Cloudflare Workers에 올라간다.
데이터베이스는 Hyperdrive를 통해 접근하는 PostgreSQL이고, 스키마는 코드가 원본이다.
영역기술백엔드Hono · Drizzle ORM · TypeScript (Cloudflare Workers)데이터베이스PostgreSQL · Hyperdrive (rtmp 스키마, drizzle-kit 마이그레이션)라이브 인프라Cloudflare Stream (Live Input · HLS/DASH egress)콘솔 UIReact 18 · Vite · TypeScript · MUI 6 · TanStack Query 5iOS 앱Swift · SwiftUI · HaishinKit · AVFoundation인증JWT(access) · Refresh rotation · bcrypt결제Stripe (구독 · 서명 검증 웹훅)배포Wrangler — API는 Worker 스크립트, 콘솔은 정적 assets
스키마 관리
스키마 정의는 백엔드 코드 안에 있고 버전 SQL은 거기서 생성된다.
Worker는 런타임에 마이그레이션하지 않는다.
스키마 적용은 배포와 분리된 별도 단계다.
도입 문의와 기술 지원은 담당 팀으로 연락해 주세요.

