두 사용자가 채팅 앱을 통해 대화를 하고 있다고 하자. 한 쪽이 메시지를 보내면 실시간으로 상대방의 앱에 해당 메시지가 나타난다.
두 사용자는 직접 연결되어 있는 것이 아니라, 서버의 중개를 통해 대화를 나누고 있다. 한 클라이언트가 메시지를 보내면 그것이 서버로 전달되고, 서버는 이를 다른 클라이언트에게 전달한다.
이 방식은 클라이언트의 수가 늘어날수록 서버에게 큰 부담이 된다.
만약 클라이언트들끼리 주고받는 데이터의 크기가 커진다면 더더욱 서버에게 버거워질 것이다.(ex. 오디오, 영상...)
서버가 감당하기 어려울 만큼 트래픽이 증가한다면 서버를 사용하기 위한 비용도 증가할 뿐 아니라 서비스도 느려질 것이다.
만약 서버의 중개 없이 클라이언트끼리 직접 소통할 수 있다면 어떻게 될까?
서버는 클라이언트들간 주고받아지는 데이터를 처리할 필요가 없어지고, 클라이언트들은 제3자를 거치지 않으니 보다 쾌적하고 빠른 소통을 할 수 있게 될 것이다.
이를 가능하게 해주는 것이 WebRTC이다.
WebRTC에서는 브라우저, 모바일 앱, 데스크탑 애플리케이션 등의 클라이언트 프로그램이 상대방과 직접 연결되어 데이터를 주고받는다. 때문에 음성통화 또는 화상채팅 기능을 제공하는 많은 서비스들이 이 WebRTC를 사용하여 구현된다.

두 사용자가 화상채팅 서비스를 통해 영상통화를 하고자 한다. 사용자들은 아직 서로에게 직접 연결되기 전이다.
이들이 peer to peer로 대화하기 위해서는 signaling이란 과정을 거쳐야 한다.
signaling이란 두 클라이언트가 무엇으로 대화할지, 그리고 어떻게 대화할지를 합의하는 과정이다.
이 signaling은 서버를 통해 이루어지는데, 이를 어떻게 구현하는지는 WebRTC가 명시하지 않는다. 개발자가 WebSocket, HTTP 등의 프로토콜들 중에 적절한 것을 선택하여 구현하면 된다.
signaling에는 SDP(Session Description Protocol)라 불리는 프로토콜이 사용된다.
SDP는 쉽게 말해 '무엇으로 소통할지', '어떻게 소통할지'를 적어 보내는 양식이다.
SDP는 세션 정보와 네트워크 관련 정보, 미디어의 유형과 스트림의 속성 등이 어떻게 작성되는지 정해져 있다.
SDP에 담기는 많은 정보들 중에 '무엇으로 소통할 것인가'가 있다. 여기에는 비디오나 오디오 등 미디어의 유형, 그들의 형식, 코덱, 해상도 등의 정보가 담긴다.
클라이언트들은 각각이 소통에 사용할 수 있는 네트워크 경로들의 정보를 보낸다. 이를 ICE(Interactive Connectivity Establishment) Candidate, 즉 ICE 후보라고 부른다. 이 후보들에는 3가지 유형이 존재한다.
- Host Candidate
- Server Reflexive Candidate
- Relay Candidate
두 사용자가 한 집이나 오피스 등, 같은 로컬 네트워크에 속한 경우이다. 여기서는 각 기기의 네트워크 내부용 주소인 사설 IP 주소와 포트 번호를 교환하면 된다.
일반적인 사용자의 클라이언트 기기는 NAT을 사용하여 네트워크에 연결되어 있다. NAT은 여러 장치가 하나의 공인 IP 주소를 통해 인터넷을 접속하는 것이다.
여기서 특정 기기와 연락을 주고받으려면 해당 기기가 연결된 라우터의 공인 IP, 그리고 해당 기기에 할당된 포트 번호를 알아야 한다. 이 둘의 조합을 각자의 주소로 하여 클라이언트들 간의 직접 소통이 이뤄지는 것이다.
문제는, 클라이언트는 이 둘을 모른다는 것이다. 게다가 이들은 보통 이따금씩 바뀔 수 있기 때문에, 당장 현재의 공인 IP 주소와 포트를 알아내야 할 필요가 있다.
이 정보를 얻기 위해 사용되는 것이 STUN(Session Traversal Utilities for NAT Server)이다.
WebRTC로 구현되는 서비스에는 특정 STUN 서버가 지정되어 있다.
클라이언트는 이 STUN 서버에 요청을 보내고, 그에 대한 응답으로 STUN 서버는 클라이언트에게 공인 IP 주소와 포트 번호를 알려준다. 즉, 상대방에게 알려줄 나의 주소를 알아내기 위해 STUN 서비스를 이용하는 것이다.
STUN은 많은 서비스 기업들에서 무료 또는 저렴한 비용으로 제공하므로 개발자가 직접 구현할 필요가 없다.
STUN 서비스를 통해 얻어온 경로 정보가 ICE의 두 번째 후보로 등록된다.
하지만 네트워크 환경과 설정에 따라, 이 방식도 어려운 경우들이 있다. 네트워크 환경이 복잡하거나, 방화벽 등에 의해 P2P 통신이 제한되어 있는 등의 경우들이다.
그럴 때 마지막 대안으로 사용되는 것이 TURN(Traversal Using Relays arount NAT) 서버이다.
클라이언트들은 이 TURN 서버를 사이에 두고, 이를 통해 소통하게 된다.
쉽게 알 수 있듯이 이는 추가적인 경로를 거치므로 지연 시간이 증가하고, 비용 또한 발생하게 되므로 마지막 옵션으로만 사용된다.
이처럼 각 클라이언트는 각자 가용한 ICE Candidate 정보를 SDP에 포함시켜 상대방에게 전달한다. 이를 통해 서로가 무엇으로, 그리고 어떻게 소통할 수 있는지 확인한 다음, 가장 최선의 방법을 선택하여 연결을 설정하고, 본격적으로 P2P 소통을 시작하게 된다.
이 과정에서의 데이터 전송은 주로 SRTP와 SCTP 중 소통의 특성과 용도에 맞는 프로토콜을 사용하게 된다.
WebRTC에서 이뤄지는 소통은 보통 화상채팅이나 음성통화 같은 것이기 때문에 이 프로토콜들은 정보의 신뢰성을 보장하기 위한 TCP보다는 빠른 대용량 전송에 적합한 UDP 기반으로 되어있다.
WebRTC에서는 비디오나 오디오의 실시간 전송에 적합한 SRTP(Secure Real-time Transport Protocol)가 가장 많이 사용된다.
하지만 파일을 전송해야 하는 경우에는 조금이라도 빠진 내용이 있으면 파일이 망가지기 때문에 UDP로는 곤란하다.
SCTP의 경우, UDP 기반이지만 TCP와 유사하게 신뢰성을 보장하도록 설계되어 있어서 파일 전송에 사용된다.
많은 사용자가 참여하는 다대다 통신의 경우에는 오히려 성능이 감소할 수 있다.
다대다 통신의 경우에는 SFU(Selective Forwarding Unit)나 MCU(Multipoint Control Unit) 등 추가적인 기술을 적용해야 한다.

다대다 통신의 구조가 되면 클라이언트의 과부하가 급격하게 증가한다.
위의 그림처럼 5명과의 통신을 유지한다면 데이터를 주고받을 연결이 한 명당 8개가 되는 것을 확인할 수 있다.
이를 해결하기 위해 SFU 구조를 활용할 수 있다. SFU 구조에서는 서버와 클라이언트가 peer 연결을 이루고, 각 클라이언트는 서버와 통신한다.
때문에 Mesh 구조와는 다르게 각 클라이언트는 서버에게만 자신의 데이터를 전송하면 되기 때문에 연결을 줄이고, 부하를 덜 수 있다.
물론 이 방식을 활용하면 서버 비용이 증가하고, 클라이언트가 증가하면 결국 각 클라이언트가 받는 부하는 증가한다는 문제가 여전히 존재한다.
SFU 구조와 유사하지만 가장 큰 차이는 서버로부터 받는 데이터 스트림도 하나만 존재한다는 것이다.
이는 중앙 서버에서 각 클라이언트로부터 받은 데이터를 가공하고, 이를 전송해야할 각 클라이언트들에게 보내주기 때문이다.
이로써 각 클라이언트들의 부하는 확연히 줄어들지만, SFU 구조보다 훨씬 더 높은 서버 비용을 요구한다. 그리고 확실히 Mesh 구조보단 실시간성이 떨어진다는 문제 또한 존재한다.