외부에서 UE 화면이 'negotiated'까지만 가고 멈춘다
언리얼 엔진 화면을 브라우저에서 WebRTC로 보는 Pixel Streaming을 띄웠습니다. 사내망에선 완벽했습니다. 그런데 외부 인터넷에서 접속하면 — 연결이 "negotiated"까지만 가고, 영상이 한 프레임도 안 왔습니다. 시그널링은 되는데 미디어가 0. 어디가 막힌 걸까요?
ICE 후보가 내부 IP뿐이었다
WebRTC는 연결 전에 ICE로 "어느 경로로 연결할까"를 협상합니다. 양쪽이 자기가 닿을 수 있는 주소(candidate)를 교환하죠. 문제는 — UE 스트리머가 내보낸 후보가 내부 IP(host candidate)뿐이었습니다.
도식 렌더링 중…
사내에선 그 내부 IP에 닿으니 잘 됐고, 외부에선 그 주소로 갈 길이 없어 협상만 끝나고 미디어가 안 흘렀습니다. "연결은 됐다는데 화면이 안 나온다"의 전형입니다.
해결 — TURN relay
NAT·방화벽 너머로 미디어를 흘리려면 중계 서버(TURN) 가 필요합니다. 양쪽이 직접 못 닿으면, TURN 서버가 가운데서 패킷을 중계합니다.
도식 렌더링 중…
핵심 설정은 세 가지였습니다.
- 내부 + 공인(public) 후보 둘 다 제공 — 내부 접속은 내부 경로(hairpin 회피), 외부는 공인 경로.
- 공인 후보는 TCP로 — 외부 망이 UDP를 막는 경우가 많아, TCP 전송으로 우회.
iceTransportPolicy: relay— 직접 연결을 포기하고 무조건 relay 경로를 쓰게 강제.
💡 내부 TURN은 hairpin 회피용 — 내부에서 공인 IP로 자기 자신에 돌아오는(hairpin) 라우팅이 막힌 환경이 많습니다. 그래서 내부 접속용 내부 TURN과 외부 접속용 공인 TURN을 둘 다 두는 게 안정적입니다.
⚠️ 외부 UDP 차단을 가정하라 — 기업·공공 와이파이는 UDP를 막는 경우가 흔합니다. WebRTC 기본은 UDP라 "내 PC에선 되는데 고객사에선 안 됨"이 여기서 납니다. 공인 후보를 TCP로도 열어두면 이 차단을 넘습니다.
증거로 확인
고친 뒤 검증은 추측이 아니라 패킷으로 했습니다 — 외부 접속 시 TURN 서버를 거친 relay 패킷이 수천 건 흘렀고, 동시에 GPU의 NVENC 인코더가 돌고 있었습니다. 내부·외부 양쪽에서 영상이 실제로 나오는 걸 확인했습니다.
한 줄 정리
📌 외부에서 WebRTC가 "negotiated"까지만 가고 미디어가 0이면, ICE 후보가 내부 IP뿐인 경우다. TURN relay(내부+공인 후보, 공인은 TCP,
iceTransportPolicy: relay)로 NAT·UDP 차단을 넘는다. 검증은 relay 패킷+NVENC로 — 추측 말고 증거로.
