연결 #
왜 WebRTC에는 전용 ‘연결’ 하위 시스템이 필요한가요? #
대부분의 애플리케이션은 클라이언트/서버 연결을 사용합니다. 이 모델에서는 서버가 안정적인 전송 주소(IP/포트)를 가져야 하며, 클라이언트가 서버에 요청하면 서버가 응답합니다.
WebRTC는 클라이언트/서버 모델이 아니라 P2P(Peer-to-Peer) 연결을 수립합니다. P2P에서는 연결 수립의 책임이 양쪽 피어에 균등하게 분산됩니다. 이는 WebRTC에서의 전송 주소(IP/포트)를 미리 가정할 수 없고, 심지어 세션 중에도 바뀔 수 있기 때문입니다. WebRTC는 가능한 모든 정보를 수집해 두 WebRTC 에이전트 간 양방향 통신을 성사시키기 위해 많은 과정을 수행합니다.
그러나 P2P 연결을 수립하는 일은 쉽지 않습니다. 서로 다른 네트워크에 있어서 직접 연결이 불가능할 수 있습니다. 직접 연결이 가능한 상황에서도 문제가 생길 수 있습니다. 어떤 경우에는 두 클라이언트가 같은 네트워크 프로토콜(UDP <-> TCP)을 쓰지 않거나, 서로 다른 IP 버전(IPv4 <-> IPv6)을 사용할 수 있습니다.
이러한 어려움에도 불구하고, WebRTC가 제공하는 특성 덕분에 전통적인 클라이언트/서버 기술 대비 다음과 같은 장점을 얻게 됩니다.
대역폭 비용 절감 #
미디어가 피어 간에 직접 전달되므로, 미디어를 중계하는 별도 서버를 운영하거나 비용을 지불할 필요가 없습니다.
더 낮은 지연 #
직접 통신이 더 빠릅니다! 모든 트래픽을 서버를 경유해 보내야 하면 전송이 느려집니다.
안전한 종단 간(E2E) 통신 #
직접 통신은 더 안전합니다. 데이터가 서버를 경유하지 않으므로, 심지어 서버 운영자를 신뢰할 필요도 줄어듭니다.
어떻게 동작하나요? #
위 과정을 표준화한 것이 ICE(Interactive Connectivity Establishment, RFC 8445)입니다. WebRTC 이전부터 존재하던 프로토콜입니다.
ICE는 두 ICE 에이전트가 통신하기에 최적의 경로를 찾는 프로토콜입니다. 각 ICE 에이전트는 자신에 도달 가능한 방법들을 공개하는데, 이를 후보(candidate)라 부릅니다. 후보는 다른 피어가 접근할 수 있다고 예상하는 전송 주소입니다. ICE는 이 후보들 중 최적의 쌍을 선택합니다.
구체적인 ICE 동작은 이 장 후반에서 다룹니다. 먼저 ICE가 왜 필요한지, 극복해야 하는 네트워크 행태를 이해하는 것이 도움이 됩니다.
실제 네트워크의 제약 #
ICE는 현실 세계 네트워크의 제약을 극복하는 데 초점을 둡니다. 해결책을 보기 전에 문제를 먼저 짚어봅시다.
같은 네트워크가 아님 #
대부분의 경우 상대 WebRTC 에이전트는 같은 네트워크에 있지 않습니다. 일반적인 통화는 서로 다른 네트워크에 있는 두 에이전트 간에 이뤄지며, 직접 연결이 없습니다.
아래는 공용 인터넷으로 연결된 두 개의 서로 다른 네트워크를 보여줍니다. 각 네트워크에는 두 개의 호스트가 있습니다.
같은 네트워크 내 호스트끼리는 연결이 쉽습니다. 192.168.0.1 -> 192.168.0.2
간 통신은 어렵지 않습니다! 외부 도움 없이도 가능합니다.
하지만 Router B
뒤의 호스트는 Router A
뒤의 대상에 직접 접근할 방법이 없습니다. Router A
뒤의 192.168.0.1
과 Router B
뒤의 동일한 IP를
어떻게 구분할까요? 둘 다 사설 IP이기 때문입니다! Router B
의 호스트가 Router A
로 트래픽을 보낼 수는 있지만, 요청은 라우터에서 끝납니다.
Router A
는 어떤 호스트로 전달해야 할지 알 수 없습니다.
프로토콜 제한 #
어떤 네트워크는 UDP를 전혀 허용하지 않거나, TCP를 허용하지 않을 수 있습니다. MTU(최대 전송 단위)가 매우 낮을 수도 있습니다. 네트워크 관리자가 바꿀 수 있는 변수가 많아 통신을 어렵게 만들 수 있습니다.
방화벽/IDS 규칙 #
깊은 패킷 검사(DPI) 등 지능형 필터링을 수행하는 경우도 있습니다. 일부 관리자는 모든 패킷을 해석하려는 소프트웨어를 사용합니다. 이 소프트웨어가 WebRTC를 이해하지 못하면, 화이트리스트에 없는 임의 포트의 UDP 패킷처럼 보인다는 이유로 차단할 수 있습니다.
NAT 매핑 #
NAT(Network Address Translation) 매핑은 WebRTC 연결을 가능케 하는 핵심 요소입니다. 이는 앞서 언급한 “같은 네트워크가 아님” 문제를 해결하여, 완전히 다른 서브넷의 두 피어가 통신하도록 합니다. 새로운 과제를 만들기도 하지만, 먼저 NAT 매핑이 어떻게 동작하는지 살펴봅시다.
릴레이나 프록시, 서버를 사용하지 않습니다. Agent 1
과 Agent 2
가 서로 다른 네트워크에 있지만, 트래픽은 완전히 통과합니다. 시각화하면 다음과 같습니다.
이 통신을 가능하게 하려면 NAT 매핑을 수립합니다. Agent 1이 포트 7000을 사용해 Agent 2와 WebRTC 연결을 수립하면,
192.168.0.1:7000
↔ 5.0.0.1:7000
바인딩이 생성됩니다. 이렇게 되면 Agent 2는 이 매핑으로 Agent 1에 트래픽을 보낼 수 있습니다. NAT의 동작은
매핑 정책에 따라 달라질 수 있으며, 주소/포트 의존적일 수도 있습니다. 이로 인해 직접 통신이 항상 가능한 것은 아니며, 이후에 설명할 TURN이 필요할 수 있습니다.
STUN #
STUN(Session Traversal Utilities for NAT, RFC 8489)은 NAT 매핑을 알아내는 표준 프로토콜입니다. STUN 패킷은 고정 크기 헤더와 속성 목록으로 구성되며, 핵심 메시지 타입은 다음과 같습니다.
- Binding Request -
0x0001
- Binding Response -
0x0101
Binding Request를 STUN 서버로 보내면, 응답으로 Binding Response를 받습니다. 이 응답에 포함된 XOR-MAPPED-ADDRESS(0x0020)
속성이
바로 생성된 NAT 매핑의 공인 IP/포트입니다. 이를 ‘Server Reflexive 후보’라고도 부릅니다.
NAT 유형 판별 #
안타깝게도 매핑이 항상 유용한 것은 아닙니다. 매핑이 주소 의존적(Address Dependent)인 경우, STUN 서버만 응답을 보낼 수 있고 다른 피어의 트래픽은 드롭될 수 있습니다. 이 경우 직접 통신에는 쓸모가 없습니다. RFC 5780은 NAT 유형을 판별하는 시험 방법을 정의합니다. 미리 직접 연결 가능성을 판단하는 데 유용합니다. 직접이 어렵다면 아래의 TURN을 사용합니다.
TURN #
TURN(Traversal Using Relays around NAT, RFC 8656)은 직접 연결이 불가능할 때의 해결책입니다. 서로 호환되지 않는 NAT 유형이거나, 동일한 프로토콜을 사용할 수 없는 경우에도 사용할 수 있습니다. 프라이버시를 위해서도 TURN을 쓸 수 있습니다. 모든 통신을 TURN을 통해 보내면 클라이언트의 실제 주소를 숨길 수 있습니다.
TURN은 전용 서버를 사용합니다. 클라이언트는 TURN 서버에 연결해 ‘할당(Allocation)’을 생성합니다. 그러면 임시 IP/포트/프로토콜인 RELAYED-ADDRESS
를
부여받고, 여기에 들어오는 트래픽은 클라이언트로 포워딩됩니다. 각 원격 피어에 대해 통신을 허용하려면 ‘Permission’을 생성해야 합니다.
TURN 수명주기 #
TURN 할당을 만들려면 다음을 수행합니다.
- 사용자 이름/비밀번호: TURN 할당에는 인증이 필요합니다.
- 할당 전송 방식: 릴레이와 피어 간 전송 프로토콜(UDP 또는 TCP)
- Even-Port: 연속 포트를 요청(일반 WebRTC에는 크게 중요하지 않음)
성공 시 응답의 속성에는 다음이 포함됩니다.
XOR-MAPPED-ADDRESS
: TURN 클라이언트의 매핑 주소. 릴레이 주소로 들어온 트래픽의 포워딩 대상RELAYED-ADDRESS
: 다른 클라이언트에게 제공할 주소(이 주소로 오면 클라이언트로 릴레이)LIFETIME
: 할당 만료 시간.Refresh
요청으로 연장 가능
ICE #
ICE(Interactive Connectivity Establishment)는 WebRTC가 두 에이전트를 연결하는 방법입니다(RFC 8445). 두 피어 사이의 가능한 모든 경로를 파악하고, 연결 유지까지 책임지는 프로토콜입니다. 후보쌍(candidate pair)은 로컬/원격 전송 주소의 쌍이며, 여기서 STUN과 TURN이 사용됩니다. 각 측은 사용할 주소들을 수집해 교환하고, 연결을 시도합니다.
ICE 에이전트 간에는 연결성 검사(connectivity checks)라 불리는 ICE 핑(STUN 기반)을 주고받아 연결을 수립합니다. 연결이 성립하면 일반 소켓처럼 임의의 데이터를 보낼 수 있습니다.
ICE 에이전트 생성 #
ICE 에이전트는 Controlling
또는 Controlled
역할 중 하나입니다. Controlling
에이전트가 최종 선택된 후보쌍을 결정합니다. 일반적으로 오퍼를
보내는 쪽이 Controlling입니다.
양쪽 모두 user fragment
와 password
를 가져야 하며, 연결성 검사를 시작하기 전에 교환되어야 합니다. user fragment
는 평문으로 전송되어 여러 ICE 세션을
디멕스하는 데 유용합니다. password
는 MESSAGE-INTEGRITY
계산에 사용됩니다. 각 STUN 패킷 끝에는 패킷 전체의 해시가 포함되며, 키로 password
를
사용합니다. 이는 패킷 인증과 변조 방지에 쓰입니다. WebRTC에서는 이 값들을 앞 장에서 설명한 세션 설명을 통해 교환합니다.
후보 수집 #
이제 도달 가능한 모든 주소(후보)를 수집합니다.
Host #
로컬 인터페이스에서 직접 수신하는 후보입니다(UDP 또는 TCP).
mDNS #
Host 후보와 유사하지만 IP를 가리지 않고 UUID 호스트명을 제공합니다. 멀티캐스트 리스너를 띄우고, 공개한 UUID 질의에 응답합니다. 같은 네트워크면 멀티캐스트로 서로를 찾을 수 있지만, 네트워크가 다르면 일반적으로 연결되지 않습니다(관리자가 멀티캐스트 트래버설을 허용하지 않는 한). 로컬 IP 노출을 막아 프라이버시를 향상할 수 있습니다.
Server Reflexive #
STUN Binding Request/Response를 통해 얻은 XOR-MAPPED-ADDRESS
로 생성되는 후보입니다.
Peer Reflexive #
알려지지 않은 주소에서 유효한(인증된) 트래픽을 받은 경우 생성되는 후보입니다. 예컨대 Host 후보 ↔ Server Reflexive 후보 간 통신에서 서브넷 외부와 통신하며 새 NAT 매핑이 생기는 경우가 이에 해당합니다. STUN 응답 형식은 피어 반사 주소를 자연스럽게 보고할 수 있습니다.
Relay #
TURN 서버에서 핸드셰이크 후 부여되는 RELAYED-ADDRESS
로 생성되는 후보입니다.
연결성 검사 #
이제 원격 에이전트의 user fragment
, password
, 후보를 모두 알게 되었으니 연결을 시도합니다! 모든 후보는 서로 페어링됩니다. 각 측에 후보가 3개라면
9개의 후보쌍이 만들어집니다.
시각화하면 다음과 같습니다.
후보 선택 #
Controlling/Controlled 에이전트는 모든 후보쌍에 트래픽을 시도합니다. 한 에이전트가 주소 의존 매핑 뒤에 있는 경우, 이 과정에서 Peer Reflexive 후보가 생길 수 있기 때문입니다.
트래픽이 오간 후보쌍은 Valid Candidate
로 승격됩니다. Controlling 에이전트는 Valid
후보쌍 중 하나를 지명(nominate)하고, 이 쌍을 대상으로 양방향
통신을 한 번 더 시도합니다. 성공하면 Selected Candidate Pair
가 되며, 세션 내내 이 쌍이 사용됩니다.
재시작 #
선택된 후보쌍이 어떤 이유로든 동작을 멈추면(NAT 매핑 만료, TURN 장애 등) ICE 에이전트는 Failed
상태로 전이합니다. 양측 모두 재시작하여 전체 과정을
다시 수행할 수 있습니다.