[PonsLink] WebRTC Mesh는 단순했지만 한계도 빨리 드러났다
WebRTC Mesh는 단순했지만 한계도 빨리 드러났다. 작은 방에서는 매력적이지만, 방이 커지고 기능이 붙으면 브라우저 부담과 signaling 복잡도가 같이 올라간다.

Mesh 한계가 커진 문제
Mesh 화상회의를 볼 때는 연결 모양보다 어느 자원이 어느 참여자에게 쌓이는지가 중요하다. Mesh에서 한 참여자는 자기 카메라 영상을 상대마다 보내야 한다. 참여자가 늘수록 업로드 부담이 선형 이상으로 커진다.
문의가 들어왔을 때 "연결 실패" 하나만 남아 있으면 NAT 문제인지, relay 전환인지, CPU 한계인지, 화면 공유 bitrate 문제인지 좁히기 어렵다.

현재 구성의 가정
초기 가정은 작고 신뢰할 수 있는 방에는 Mesh가 가장 단순하다는 것이었다. 중계 서버 없이 브라우저끼리 붙으니 운영 비용도 낮다.
PonsLink의 실시간 세션을 떠올리면 작은 방에서는 Mesh 감각이 잘 맞는다. 특히 비용과 복잡도를 낮추고 싶은 초기엔 good default였다.

한계가 선명해진 순간
부하가 쌓인 건 미디어 데이터 자체보다 클라이언트 자원이었다.
- CPU: 여러 인코딩/디코딩 스트림
- 업로드: 참여자 수 × 송출 스트림
- packet loss: 모바일 배터리·신호에 민감
- 화면 공유: bitrate 급증
이들은 같은 방 안에서 서로 다른 실패로 나타난다. CPU 포화는 정지, 업로드는 버퍼링, packet loss는 프레임 드롭. 하나의 "끊김"으로 합치면 원인을 못 찾는다.
Mesh 경계 선택
언제까지 Mesh로 둘 것인지 정해야 한다.
| 항목 | Mesh 한계 | 전환 검토 |
|---|---|---|
| 참여자 수 | 3~4명 이상 급격히 무거워짐 | SFU 검토 |
| 화면 공유 | bitrate 폭주 | 조건부 허용 |
| 모바일 | 배터리·신호 취약 | 품질 자동 하향 |
| 품질 제어 | 중앙 조절 불가 | SFU 필요 시점 |
Mesh가 관여하는 경로에서는 "일단 동작"보다 "중단 후에도 설명 가능"이 우선이었다. 내부 로그에는 ICE candidate, selected pair, bitrate, packet loss, decoder dropped frame이 남아야 한다.
다시 짜 본 구조: repo 분리
로컬 소스는 pons_p2p/ponslink-room-frontend와 pons_p2p/ponslink-api-infra로 나뉘어 있다. GitHub에도 ponslink-room-frontend, ponslink-api-infra, ponslink-mesh-room, ponslink_signal 흐름이 따로 남아 있다.
화면에서 보이는 방, API에서 지키는 운영 경계, 연결을 시작하게 만드는 signaling이 서로 다른 속도로 변했기 때문에 그렇게 갈라졌다. 경계가 코드에 먼저 드러나야 운영 언어도 따라온다.
실패 모드
- NAT 뒤 참여자 → relay 전환 필요
- 한쪽 CPU 포화 → 방 전체 체감 저하
- 모바일 백그라운드 → track 정지
- 화면 공유와 카메라 충돌
이들을 같은 "연결 실패"로 합치면 디버깅이 불가능하다. 상태 머신 로그와 사용자 문장을 같은 축으로 맞춰야 한다.
마치며
WebRTC Mesh는 단순하지만, 그 단순함은 작은 방에서만 유효하다. 방이 커지면 단순함이 클라이언트 부담으로 되돌아온다.
지금은 Mesh를 good default로 두되, 참여자 수·화면 공유·모바일 조건에서 SFU 전환 시점을 정해 둔다. 경계를 코드와 운영 언어에 같이 남기는 게 Mesh의 한계를 다루는 방법이다. 다음에 Mesh를 다시 쓰면 전환 시점을 먼저 문서화하겠다.