RTMP 라이브 스트리밍 플랫폼 (토이)
라이브 스트리밍(RTMP/HLS)을 직접 다뤄보려 SRS + NestJS + Vue로 만든 스트리밍 관리 플랫폼. RTMP를 내재화하면 라이브커머스의 NCP LiveStation과 노후 방송 송출 솔루션 두 가지를 한 번에 대체할 수 있어, 그 사전 학습·검증용으로 만든 토이 프로젝트입니다.
문제
회사의 스트리밍 계층은 두 갈래로 외부 솔루션에 묶여 있었다. 하나는 라이브커머스의 RTMP 송출로, NCP LiveStation(관리형)을 쓰고 있었다. 다른 하나는 TV방송을 앱/웹으로 내보내는 송출로, 5년 전 구축한 온프레미스 설치형 솔루션에 의존했는데 — 라이선스상 동시 시청 제한이 있고, RTMP fragment 캐싱 확장에도 한계가 있으며, ABR(적응형 화질)조차 없었다. 즉 RTMP 인제스트를 자체 구축해 내재화하면 LiveStation과 노후 온프레미스 솔루션 두 가지를 한 번에 대체할 수 있는 셈이었다. 라이브커머스 영상 인프라를 내재화했듯 스트리밍도 언젠가 가져오려면, 먼저 RTMP→트랜스코딩→HLS 파이프라인이 실제로 어떻게 도는지 몸으로 알아야 했다. 그 학습과 검증을 실서비스 리스크 없이 해볼 토이가 필요했다.
접근
클라우드 라이브 서비스(NCP Live Station·AWS MediaLive·GCP Live Stream)와 오픈소스 미디어 서버를 먼저 문서로 비교하고, RTMP 인제스트 서버로 SRS(vs Nginx-RTMP)를 선정한 뒤 실제로 굴러가는 플랫폼을 직접 만들었다. OBS로 RTMP를 SRS에 밀어넣으면, SRS webhook(on_publish 등)이 NestJS로 오고 스트림키를 SHA-256으로 인증한 뒤 채널 상태를 IDLE→CONNECTING→LIVE로 전이시킨다. 트랜스코딩은 SRS exec로 단일 FFmpeg가 1080p/720p/480p 3해상도를 동시 출력하고, NestJS가 ABR Master Playlist(m3u8)를 동적 생성해 시청자에게 자동 화질 전환을 제공한다. 녹화는 FFmpeg로 FLV로 받아 재인코딩 없이 MP4로 리먹싱(faststart)했고, 세션은 Redis TTL + 좀비 감지 크론(분산 락)으로 관리했다. 스트림키는 AES-256-GCM으로 저장하고 DB는 마이그레이션(DDL)·런타임(DML) 계정을 분리하는 등, 실무에서 쓰던 소유·보안 원칙을 토이에도 그대로 적용했다. Turborepo+pnpm 모노레포에 API/Web/공유패키지를 나누고 테스트 121개를 붙였다.
결과
RTMP 인제스트부터 ABR 트랜스코딩·HLS 송출(지연 5~6초)·녹화·세션·모니터링(Prometheus)까지 도는 스트리밍 플랫폼을 손에 넣었다. 무엇보다 상용 솔루션이 블랙박스로 감추던 부분 — 스트림키 인증 흐름, webhook 이벤트 모델, exec 트랜스코딩 구성, HLS 세그먼트/윈도우가 지연에 미치는 영향 — 을 직접 만지며 이해하게 됐다. 실제 내재화를 결정할 때 근거 있는 기술 선택(SRS·SRT/WebRTC 확장성 등)과 아키텍처를 이 검증 위에서 바로 제안할 수 있다.
배운 것
스트리밍은 '영상이 나온다'까지는 쉽지만, 지연·화질전환·세션 정합·재접속을 다루기 시작하면 진짜 복잡도가 드러났다. HLS fragment/window 같은 파라미터 하나가 지연시간을 좌우하고, RTMP 연결의 생사를 webhook만으로 판정하면 좀비 세션이 남는다는 걸 직접 겪었다. 토이의 목적은 완성이 아니라 '내재화가 현실적인가'를 미리 가늠하는 것이었고, 답은 '가능하다 — 단 트랜스코딩 비용과 이중화가 관건'이었다. 미리 만들어 본 덕에, 나중에 실서비스 내재화 논의가 나오면 추측이 아니라 검증된 경험으로 말할 수 있게 됐다.
왜 만들었나
라이브커머스 영상 인프라를 내재화(Kollus 이관)했지만, 스트리밍 계층 자체는 여전히 외부 솔루션 두 갈래에 묶여 있었습니다.
- 라이브커머스 RTMP 송출 — NCP LiveStation(관리형) 사용.
- TV방송 → 앱/웹 송출 — 5년 전 구축한 온프레미스 설치형 솔루션. 라이선스상 동시 시청 제한, RTMP fragment 캐싱 확장 한계, ABR 없음.
즉 RTMP 인제스트를 자체 구축하면 이 둘을 한 번에 내재화할 수 있는 셈이었습니다. 라이브커머스 영상 인프라를 내재화했듯 스트리밍도 언젠가 우리 손으로 가져오려면, 먼저 파이프라인이 실제로 어떻게 도는지 알아야 했습니다. 그래서 실서비스 리스크 없이 스트리밍을 처음부터 끝까지 굴려보는 토이를 만들었습니다. 목적은 두 가지 — ① RTMP/HLS 지식 습득, ② 스트리밍 솔루션(LiveStation + 온프레미스 방송) 내재화 사전 검증.
기술 선정 (문서 먼저)
코드를 짜기 전에 문서로 비교부터 했습니다.
- 클라우드 vs 온프레미스 — NCP Live Station · AWS MediaLive · GCP Live Stream API의 인제스트/트랜스코딩/이중화 기능을 비교하고, 내재화 시 필요한 기능 우선순위(Phase 1~3)를 정리.
- 미디어 서버 선정 — RTMP 인제스트 서버로 SRS 6.x vs Nginx-RTMP를 비교. Nginx-RTMP는 2019년 이후 사실상 유지보수만인 반면, SRS는 활발히 개발되고 **SRT·WebRTC(WHIP/WHEP)**까지 지원해 확장성에서 우위 → SRS 채택.
동작
OBS ──RTMP──▶ SRS (ingest·exec 트랜스코딩·HLS)
│ webhook(on_publish 등) │ HLS
▼ ▼
NestJS API ◀─폴링─ SRS Nginx ──▶ 시청자
│ Prisma
▼
PostgreSQL · Redis Vue3 + PrimeVue 관리 콘솔
- 스트림키 인증 — UUID 발급 → SHA-256 해시로 on_publish webhook 인증, AES-256-GCM 암호화 저장, 재발급 시 즉시 무효화.
- ABR 트랜스코딩 — SRS
exec로 단일 FFmpeg가 1080p(4M)/720p(2M)/480p(1M) 동시 출력, NestJS가 Master Playlist를 동적 생성해 자동 화질 전환. - 녹화 — FFmpeg로 FLV 녹화(안정성) 후 재인코딩 없이 MP4 리먹싱(
+faststart). - 세션 — Redis TTL 기반 상태(CONNECTING/LIVE/DISCONNECTED) + 15초 크론 좀비 감지(분산 락).
- 모니터링 — 대시보드 + Prometheus 엔드포인트, 채널별 접속자·비트레이트.
실무 원칙을 토이에도
토이라도 실무에서 쓰던 원칙을 그대로 적용했습니다.
- DB 계정 분리 — 마이그레이션(DDL)
streaming_admin≠ 런타임(DML)streaming_app로 앱의 DDL 권한 제거. - 보안 — 스트림키 AES-256-GCM 저장, JWT 인증(브루트포스 보호), 감사 로그.
- 품질 — Turborepo+pnpm 모노레포(API·Web·shared), 테스트 121개.
배운 것
- 파라미터가 지연을 지배한다 — HLS
fragment(2s)·window(10s) 조합으로 체감 지연이 5~6초로 결정됐습니다. - 연결의 생사 판정이 어렵다 — webhook만으론 비정상 종료를 놓쳐 좀비 세션이 남았고, Redis TTL + 좀비 감지로 메꿔야 했습니다.
- 내재화는 가능하다, 단 조건부 — 트랜스코딩 CPU 비용과 이중화가 관건. 미리 검증해 둔 덕에, 실서비스 내재화 논의가 나오면 추측이 아니라 경험으로 아키텍처를 제안할 수 있게 됐습니다.