GStreamer는 정상인데 왜 수신이 실패했나: 로봇 WebRTC 장애를 경계로 분해한 디버깅
실시간 스트리밍 문제는 “안 된다"로 시작하면 끝이 없다. 특히 로봇 영상 파이프라인은 네트워크, 코덱, 프로토콜, 서버 정책이 동시에 얽혀 있어 한 번에 고치려 하면 더 느려진다.
이번 글은 GStreamer 송신은 되는데 서버에서 거부되는 상황에서
어떻게 원인을 좁혀갔는지 기록한 트러블슈팅 노트다.
증상
당시 관찰한 핵심 증상은 두 가지였다.
- 로컬/별도 UDP echo 테스트에서는 업로드가 정상
- 실제 서버 경로에서는 UDP/RTP 수신이 일부 거부됨
즉, “로봇 송신 코드가 완전히 틀렸다"보다
서버 경계에서 요구하는 연결 조건을 충족하지 못했다 쪽이 더 유력했다.
가설 분해
문제를 세 가지 축으로 쪼갰다.
- 미디어 송신 축: GStreamer 파이프라인, SRTP key, payload 설정
- 프로토콜 축: RTP/SRTP, Transport 연결 절차, candidate/port 협상
- 서버 정책 축: 보안 제한, 입력 허용 조건, 내부 라우팅 정책
이렇게 나누면 “어디가 문제인지 모른다"가 아니라 “어느 축에서 실패하는지"를 확인할 수 있다.
판단에 도움된 포인트
실무에서 유효했던 신호는 아래였다.
- UDP echo가 통과되면 인코딩/기본 송신은 대체로 정상
- 서버에서만 막히면 정책/협상 절차 문제일 가능성이 높음
- Mediasoup Transport는 단순 포트 개방이 아니라 시그널링 절차가 핵심
정리하면, RTP를 쏘는 것과 WebRTC 경로에 정합하게 연결되는 것은 다른 문제였다.
실제 대응 전략
당시 정리한 대응 순서는 이렇다.
- 로컬 mediasoup 환경으로 동일 파이프라인 재현
- 서버와 로컬의 입력 조건 차이를 체크리스트로 비교
- 로봇 Transport 경로를 사용자 경로와 분리해 정책 단순화
- 필요 시 최소 WebRTC 절차(시그널링/후보 교환)로 경로 전환
핵심은 기능을 더 붙이는 것이 아니라,
경계를 분리해 디버깅 가능한 상태를 만드는 것이었다.
RCA 템플릿(재발 방지용)
장애가 끝난 뒤 반드시 아래 항목을 남겼다.
- 증상 발생 시각/구간
- 재현 조건(네트워크, 포트, ICE 타입)
- 1차 원인과 2차 증폭 요인
- 임시 조치와 근본 조치 분리
- 검증 시나리오(재현 테스트 케이스)
이 템플릿이 있어야 다음 장애에서 “추측” 대신 “비교"가 가능해진다.
배운 점
이 이슈에서 가장 큰 교훈은 다음이다.
- 실시간 장애는 대부분 “단일 버그"가 아니라 “경계 불일치"다.
- 송신 코드 최적화보다 서버 수용 조건 명문화가 먼저다.
- 트러블슈팅 기록 자체가 재발 방지 문서가 된다.
참고 및 인용
참고: Mediasoup 문서는 Transport 연결 절차와 시그널링 분리 원칙을 상세히 설명한다. Communication Between Client and Server
참고: WebRTC 표준은 ICE/DTLS/SRTP를 포함한 종단 간 연결 성립 조건을 정의한다. WebRTC 1.0
참고: GStreamer 문서는 파이프라인 단위로 인코딩/전송 문제를 분리 진단할 때 기본 레퍼런스다. GStreamer Documentation
시리즈
- 1편: 운영 가능한 로봇 Agent는 프로비저닝에서 시작된다
- 2편: 앱 서버 병목에서 MQTT 브로커 분리까지
- 3편: 기능보다 먼저 테스트 가능한 구조
- 4편(현재 글): 로봇 WebRTC 스트리밍 장애를 푸는 방법