1️⃣ 들어가며: 왜 WebRTC인가? (삽질의 시작)

처음에는 RTSP CCTV 영상을 웹 브라우저에 띄우기 위해 Node.js 기반의 HLS(HTTP Live Streaming) 서버를 구축했습니다. 구현은 어렵지 않았지만, 막상 운영해보니 치명적인 단점이 발견되었습니다.
- 문제점: 영상 딜레이가 심하고, 스트리밍이 간헐적으로 뚝뚝 끊기는(Stuttering) 현상 발생.
- 판단: "실시간 감시용으로 HLS는 한계가 있다. WebRTC로 넘어가자."
🤔 왜 HLS는 느리고 끊길까? HLS는 영상을 잘게 쪼개서(Chunk) 파일로 만든 뒤 다운로드하는 방식입니다. 태생적으로 **최소 3~10초 이상의 지연(Latency)**이 발생할 수밖에 없는 구조입니다. 반면, WebRTC는 P2P 기반의 실시간 통신을 위해 만들어져 **1초 미만의 초저지연(Ultra Low Latency)**이 가능합니다. CCTV 관제에는 WebRTC가 정답이었습니다.
결국 오픈소스 RTSP/WebRTC 서버인 MediaMTX를 도입하기로 결정했습니다. 하지만 이 과정도 순탄치 않았는데, Docker 설정 문제와 UDP 포트 이슈, 기존 서버(Nginx) 충돌 등 여러 난관을 극복한 과정을 기록으로 남깁니다.
2️⃣ 서버 환경 정보
이 작업은 클린한 서버가 아니라, 이미 수많은 테스트 프로젝트가 돌아가고 있는 **'혼잡한 개발 서버'**에서 진행되었습니다.
- OS: Ubuntu 24.04.3 LTS
- 서버 상태: 다수의 웹 서비스(Nginx 등) 및 미들웨어가 실행 중
- MediaMTX 버전: v1.15.6
- 목표: RTSP 영상을 WebRTC로 변환하여 끊김 없는 스트리밍 구현
3️⃣ Docker를 포기하고 바이너리를 선택한 이유
처음엔 국룰처럼 Docker 이미지를 사용하려 했습니다. 하지만 MediaMTX 최신 버전(v1.15.6)을 적용하는 과정에서 문제가 터졌습니다.
- 설정 파일 민감성: MediaMTX는 버전별로 mediamtx.yml 설정 키값이 매우 엄격하게 변합니다.
- 무한 재부팅: Docker 컨테이너로 띄우니 설정 파일 에러가 날 때마다 Restarting... 루프에 빠져 로그 확인과 디버깅이 너무 힘들었습니다.
💡 나의 결론: 배포 단계에서는 Docker가 편하지만, 초기 세팅이나 디버깅 단계에서는 블랙박스 같은 컨테이너보다 로그를 눈으로 직접 볼 수 있는 바이너리 실행이 훨씬 효율적이라 판단했습니다. "남들이 다 쓴다고 무작정 Docker를 쓰는 게 능사는 아니다"라는 걸 이번에 확실히 느꼈습니다.
4️⃣ MediaMTX 설치 및 설정 (v1.15.6 기준)
1. 다운로드 및 설치
GitHub 릴리즈에서 바이너리를 받아 /opt 경로에 설치했습니다.
# 다운로드 & 권한 부여
wget https://github.com/bluenviron/mediamtx/releases/download/v1.15.6/mediamtx_linux_amd64
chmod +x mediamtx_linux_amd64
mv mediamtx_linux_amd64 mediamtx

2. 설정 파일(mediamtx.yml)의 함정
💡 설정 포인트:
- webrtcLocalUDPAddress: :8189: UDP 포트를 8189로 고정하여 방화벽 관리를 편하게 했습니다.
- webrtcAdditionalHosts: 외부 망에서 접속할 때, 서버가 자신의 공인 IP를 클라이언트에게 알려주도록 설정했습니다. (이거 안 하면 외부에서 접속 불가)
logLevel: info
logDestinations: [stdout]
rtsp: yes
rtspAddress: :8554
webrtc: yes
webrtcAddress: :8082
webrtcLocalUDPAddress: :8189 # UDP 포트 고정
webrtcAdditionalHosts:
- 124.61.xx.xx # [중요] 본인 서버의 공인 IP 입력 (외부 접속용)
webrtcICEServers:
- stun:stun.l.google.com:19302
hls: no
paths:
cctv1:
source: rtsp://210.99.xx.xx:1935/live/cctv001.stream
sourceOnDemand: yes
cctv2:
source: rtsp://210.99.xx.xx:1935/live/cctv007.stream
sourceOnDemand: yes
🚨 [필독] 검은 화면 해결: 방화벽 및 UDP 포트 포워딩
이 부분을 놓치면 100% 검은 화면만 보게 됩니다. WebRTC 설정에서 가장 많이 하는 실수가 TCP 포트만 열고 UDP를 안 여는 것입니다.
- WebRTC 연결 (Signaling): TCP (8082)
- 실제 영상 데이터 (Media): UDP (8189)
설정 파일(mediamtx.yml)에 적힌 8082(TCP)만 열어주면, 플레이어 UI는 뜨는데 영상이 무한 로딩(검은 화면)되는 현상을 겪게 됩니다.
✅ 필수 조치 사항
반드시 공유기나 방화벽에서 UDP 8189 포트를 열어줘야 영상이 정상적으로 들어옵니다.
- 방화벽(UFW) 오픈:
# (Ubuntu UFW 사용 시 예시) sudo ufw allow 8189/udp - 공유기 포트포워딩:
- 외부 포트: 8189 (UDP)
- 내부 IP: 서버 IP
- 내부 포트: 8189 (UDP)
⚠️ 주의: TCP가 아니라 UDP입니다! 프로토콜 타입을 꼭 확인하세요.
6️⃣ 트러블슈팅: Nginx와의 포트 충돌
설정을 마치고 야심 차게 ./mediamtx를 실행했으나, 빨간 에러 로그가 반겨주었습니다.
ERR listen tcp :8082: bind: address already in use
**"주소가 이미 사용 중"**이라는 뜻입니다. 앞서 말했듯 이 서버는 이미 다른 프로젝트들이 많이 떠 있는 상태였습니다. 범인을 찾아봤습니다.
sudo lsof -i :8082
# [출력 결과]
# nginx ... TCP *:8082 (LISTEN)
범인은 Nginx였습니다. 기존에 떠 있던 웹 서비스 중 하나가 8082 포트를 점유하고 있어서, MediaMTX의 WebRTC 포트와 충돌이 난 것입니다.
💡 해결: 당장 Nginx가 필요 없었기에 과감하게 중지시켰습니다.
sudo systemctl stop nginx
sudo systemctl disable nginx
이후 MediaMTX가 정상적으로 포트를 바인딩하며 실행되었습니다.
7️⃣ 마무리: Systemd 등록 및 결과
마지막으로 서버가 재부팅되어도 자동으로 켜지도록 systemd 서비스를 등록했습니다.
# /etc/systemd/system/mediamtx.service
[Unit]
Description=MediaMTX Streaming Server
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/mediamtx
ExecStart=/opt/mediamtx/mediamtx
Restart=always
[Install]
WantedBy=multi-user.target

📈 최종 요약
- Node.js HLS: 딜레이 심함, 끊김 발생 ❌
- MediaMTX WebRTC: 딜레이 거의 없음, 매우 부드러움 ✅
- 주의사항: 반드시 UDP 8189 포트를 열어야 영상이 나옴 (TCP만 열면 검은 화면)
혹시 저처럼 RTSP 스트리밍을 구현하다가 "왜 자꾸 끊기지?" 고민하신다면 HLS를 붙잡고 있지 말고 **WebRTC(MediaMTX)**로 넘어오시는 걸 강력 추천합니다. 그리고 설정이 꼬일 땐 Docker 말고 바이너리로 먼저 테스트해보세요!
'Trouble Shooting (에러 해결)' 카테고리의 다른 글
| [Trouble Shooting] Firebase 설정 중 CONFIGURATION_NOT_FOUND 에러 해결 기록 (0) | 2026.01.29 |
|---|