공인 IP는 하나인데 공유기 뒤에는 기기가 열 대가 넘습니다. 내 iPad는 세 네트워크나 떨어진 모바일 데이터에 있습니다. 그런데 어떻게든 원격 데스크톱 앱은 1초도 안 되어 한쪽에서 다른 쪽으로 직접 연결을 엽니다. 보통은 중간에 릴레이도 없이요. 그 마법이 실제로 어떻게 동작하는지, ICE, STUN, TURN, 홀 펀칭, 그리고 대칭 NAT에 얽힌 곤란한 진실을 설명합니다.
왜 중요한가
화요일 아침을 떠올려 보세요. Mac mini는 공인 IPv4 주소가 하나뿐인 공유기 뒤의 집에 있습니다. iPad는 기차 안에서, 자체 주소 변환 계층을 가진 통신사 모바일 네트워크에 있습니다. 두 기기 사이에는 NAT가 최소 두 개, 아마 세 개 있습니다. 어느 컴퓨터도 기본적으로 바깥 인터넷에서는 닿을 수 없습니다.
그런데도 iPad에서 Remio를 열고 Mac을 탭하면 1초 뒤에 데스크톱이 보입니다. 비디오와 입력은 두 기기 사이를 직접 오갑니다. 클라우드 릴레이도, 화면을 다시 인코딩하는 프록시도 중간에 없습니다.
이를 가능하게 하는 기술에는 NAT 순회라는 이름이 있고, 그 뒤에는 STUN, TURN, ICE라는 잘 정의된 프로토콜 군이 있습니다. 브라우저 화상 통화에서 WebRTC가 쓰는 바로 그 프로토콜이며, 현대적인 P2P 원격 데스크톱이 화면 프레임을 제3자의 데이터 센터로 돌리지 않아도 되는 이유입니다. 이 글은 각각이 무엇을 하는지, 왜 있는지, 그리고 이들이 어떻게 결합해 공유기 뒤에 숨은 두 기기 사이에서 지연이 가장 낮은 경로를 찾는지 차근차근 보여 줍니다.
60초로 보는 NAT 문제
인터넷은 모든 기기가 자신만의 공개 라우팅 가능한 주소를 갖도록 설계되었습니다. IPv4 주소는 전체 공간에 약 43억 개입니다. 1981년에는 충분했지만, 1990년대 후반에는 분명히 부족했습니다.
그 우회책이 네트워크 주소 변환(NAT)이었습니다. 공유기는 통신사로부터 공인 IPv4 주소를 하나 받습니다. 집 안에서는 모든 기기가 예약된 범위(보통 192.168.x.x나 10.x.x.x)에서 사설 주소를 받습니다. 노트북이 패킷을 밖으로 보내면 공유기는 출발지 주소를 자신의 공인 IP로 다시 쓰고, 그 대응을 기억한 채 전달합니다. 응답이 돌아오면 공유기는 대응을 찾아 목적지를 다시 노트북으로 되돌립니다.
바로 이것이 들어오는 연결이 기본적으로 안 되는 이유입니다. 밖에서 보면 노트북에는 주소가 없고 공유기만 있습니다. 낯선 사람이 your-router-public-ip:5060으로 보낸 패킷은 대응이 없는 공유기에 닿아 버려집니다.
P2P 앱에게는 이것이 게임의 전부입니다. 양쪽 피어는 먼저 밖으로 뭔가 보낸 것을 기억할 때만 패킷을 전달하는 공유기 뒤에 있습니다. 어느 쪽도 상대를 그냥 "호출"할 수 없고, 공유기와 협력해 임시 경로를 열어야 합니다.
홀 펀칭: 방화벽이 다시 들여보내게 하기
요령은 이렇습니다. 공유기의 NAT 테이블은 출발지 IP, 출발지 포트, 목적지 IP, 목적지 포트, 프로토콜이라는 5-튜플을 키로 씁니다. 기기가 1.2.3.4:5678로 UDP 패킷을 밖으로 보내면 공유기는 "1.2.3.4:5678에서 내 공인 IP의 X 포트로 UDP 패킷이 오면 이 기기의 Y 포트로 전달하라"는 임시 항목을 만듭니다.
이 항목은 일정 시간, 보통 30~60초 동안 존재하다가 무언가 갱신하지 않으면 만료됩니다. 그 시간 동안에는 목적지 주소에서 오는 어떤 패킷이든 받아들여집니다.
P2P 앱이 쓰는 열쇠 구멍이 이것입니다. 양쪽 피어가 서로의 공인 주소를 안다면 같은 순간에 각자 상대에게 밖으로 향하는 UDP 패킷을 하나씩 보낼 수 있습니다. 각 공유기가 아웃바운드 대응을 엽니다. 각자의 첫 패킷은 상대 공유기가 아직 대응을 열지 않아 버려질 수 있지만, 두 번째 패킷은 갈 곳이 있습니다. 그 순간부터 두 공유기는 평범한 아웃바운드 대화를 전달하고 있다고 생각하고, 두 기기는 직접 대화합니다.
이를 UDP 홀 펀칭이라고 합니다. 조금 기적 같고, 잘 문서화되어 있으며(RFC 5128), 실제 대부분의 소비자용 NAT에서 동작합니다. 남는 경우, 즉 대칭 NAT, CGNAT, 기업 방화벽이 TURN이 존재하는 이유입니다.
홀 펀칭은 편법이 아닙니다. NAT가 설계된 명시적인 약속, 즉 아웃바운드 패킷이 인바운드 허가를 만든다는 것을 따릅니다. P2P 앱은 그 아웃바운드 패킷을 일부러 보낼 뿐입니다.
STUN: "내 공인 주소가 뭐지?"
홀 펀칭에는 전제가 하나 있습니다. 각 피어가 상대에게 패킷을 어디로 보내야 하는지 알려 주려면 자신의 공개 주소를 알아야 합니다. 그 주소는 기기 안에서는 보이지 않습니다. iPad가 보는 것은 로컬 Wi-Fi 주소(192.168.1.42)입니다. 공유기가 패킷을 다시 쓴 뒤 나머지 인터넷이 보는 것은 전혀 다른 주소입니다.
STUN, 즉 NAT용 세션 순회 유틸리티(RFC 5389)는 이를 해결하는 작은 프로토콜입니다. 흐름은 민망할 만큼 단순합니다.
- 피어가 공개 STUN 서버로 UDP 패킷을 보냅니다. (Remio는 이중화를 위해 Cloudflare와 Google의 공개 STUN 서버를 모두 씁니다.)
- STUN 서버는 회선에서 출발지 IP와 포트를 읽습니다. 정의상 이것이 피어의 NAT로 변환된 공인 주소입니다.
- STUN 서버가 그 주소를 내용으로 답합니다. "제가 보기에 당신은
203.0.113.42:48291입니다." - 이제 피어는 상대에게 어떤 주소를 알려야 하는지 압니다.
이 공개 주소를 ICE 용어로 서버 반사 후보라고 합니다. 비용은 사실상 없습니다. 연결 설정 중 왕복 한 번이고, 연결이 맺어진 뒤에는 대역폭도 지연도 더해지지 않습니다. STUN의 유일한 임무는 두 피어를 서로에게 소개하는 것입니다. 대화가 시작되면 STUN 서버는 더 이상 역할이 없습니다.
TURN: 홀 펀칭이 실패할 때
홀 펀칭은 대부분의 NAT에서 동작하지만 전부는 아닙니다. 실패하는 경우는 실재하며, 그런 경우가 없는 척하는 P2P 앱은 상당수 사용자에게 연결 자체가 안 됩니다.
주요 악당들은 다음과 같습니다.
- 대칭 NAT. 목적지마다 다른 공인 포트를 할당하는 NAT입니다. STUN은 내가 48291 포트에 있다고 알려 주지만(STUN 서버가 본 포트이므로), 상대 주소로 패킷을 보낼 때 공유기는 다른 포트, 예컨대 48317을 고릅니다. 상대의 공유기는 예상치 못한 포트에서 온 패킷이라며 거부합니다. 홀 펀칭이 깨집니다.
- 통신사급 NAT (CGNAT). 모바일과 많은 ISP 네트워크는 통신사 수준에서 두 번째 NAT를 겹쳐 둡니다. 모르는 사이 NAT 두 개 뒤에 있을 수 있고, 둘이 모두 협력해야 합니다. CGNAT는 흔히 대칭이고 관리자 권한도 없습니다.
- 제한적인 기업 방화벽. 일부 기업 네트워크는 아웃바운드 UDP를 전부 막거나 특정 목적지로만 허용합니다. 홀 펀칭에는 UDP 경로가 필요하고, 없으면 가망이 없습니다.
이런 경우의 대체 수단이 TURN, 즉 NAT 주변 릴레이를 이용한 순회(RFC 5766)입니다. TURN 서버는 항상 인바운드 트래픽을 받는 공인 주소를 가졌기에 양쪽 피어가 모두 닿을 수 있는 릴레이입니다. 각 피어는 TURN 서버로 평범한 아웃바운드 TLS 연결을 맺고(어떤 NAT든 허용합니다), 서버가 두 쪽 사이에서 바이트를 전달합니다.
TURN에는 실제 비용이 있습니다. 비디오 패킷마다 네트워크 단계를 하나 더 거쳐야 합니다. 같은 지역 TURN 릴레이에서는 왕복 지연이 약 30~80밀리초 늘어납니다. 지역을 가로지르면, 예컨대 도쿄의 피어와 상파울루의 피어를 위한 프랑크푸르트 릴레이는 80~200밀리초가 될 수 있습니다. TURN 운영자도 대역폭 비용을 냅니다. 스트림의 모든 비트가 그들의 관을 지나가기 때문입니다.
그래서 잘 설계된 앱은 홀 펀칭이 확실히 실패했을 때만 TURN을 씁니다. 그 판단은 ICE가 자동으로 처리합니다.
ICE: 모든 후보를 병렬로 시도
대화형 연결 수립(ICE), RFC 8445는 이 모든 것을 엮는 안무입니다. ICE는 전략을 미리 고르지 않습니다. 각 피어에 대해 가능한 모든 후보 주소를 모으고, 전체 목록을 상대와 교환한 뒤, 양쪽 스택이 모든 후보 쌍을 병렬로 탐색해 먼저 동작하는 것을 찾는 경주를 합니다.
각 피어에 대해 ICE는 보통 다음을 모읍니다.
- 호스트 후보 — 모든 네트워크 인터페이스(이더넷, Wi-Fi, 가상 인터페이스, IPv4와 IPv6)의 모든 로컬 IP 주소.
- 서버 반사 후보 — STUN 서버에서 알아낸 공인 주소.
- 릴레이 후보 — TURN 서버에 할당된 주소.
두 피어는 시그널링 채널로 이 목록을 교환합니다. Remio에서는 relay.remio.net으로 가는 작은 WebSocket 연결이 ICE 후보와 WebRTC 세션 설명(SDP)을 나르고, 미디어가 흐르기 시작하면 더 할 일이 없습니다.
두 목록이 모이면 ICE는 연결성 검사를 시작합니다. 각 피어는 우선순위 순서로 모든 후보 쌍에 STUN 바인딩 요청 패킷을 보냅니다. 우선순위는 지연이 가장 낮은 경로를 선호하도록 설계되어 있습니다.
- 호스트 대 호스트 — 두 피어가 같은 로컬 네트워크에 있는 경우입니다. 꿈의 시나리오로, 지연은 수백 마이크로초이고 공유기가 끼지 않습니다.
- 호스트 대 서버 반사, 또는 서버 반사 대 서버 반사 — 피어가 서로 다른 네트워크에 있고 각자의 NAT를 홀 펀칭으로 통과한 경우입니다. 실제로 "인터넷을 가로지르는 P2P"라고 하면 보통 이것입니다.
- 릴레이가 끼는 모든 경우 — 최후의 수단으로, 우선순위가 높은 경로가 모두 연결성 검사에서 실패했을 때만 씁니다.
양방향 모두 성공한 첫 후보 쌍이 승자로 지정되고, 그 경로로 미디어가 흐르기 시작합니다. 전체 과정은 보통 1초가 훨씬 안 되어 끝납니다.
ICE는 후보도 계속 모읍니다. 세션 도중 더 나은 경로가 생기면, 예컨대 휴대폰이 모바일 데이터에서 Wi-Fi로 옮겨 가면, ICE는 통화를 끊지 않고 새 경로로 다시 지정할 수 있습니다.
Remio가 P2P를 강하게 선호하는 이유
Remio는 화상 통화가 아니라 원격 데스크톱 앱입니다. 지연 예산은 가혹합니다. 세션이 호스트 컴퓨터 앞에 앉은 것처럼 느껴지려면 캡처, 인코딩, 네트워크, 디코딩, 렌더링 전체 파이프라인이 아주 작아야 합니다. LAN에서는 약 8ms가 들고, 나머지는 디스플레이 자체의 60Hz 주사율 주기(프레임당 약 16.7ms)입니다. 단계 하나가 늘 때마다 죄가 됩니다.
직접 LAN P2P 연결에서 이 예산 중 네트워크 몫은 왕복 약 1~5밀리초입니다. 릴레이가 없고 바이트가 한 기기에서 다른 기기로 곧장 갑니다. Remio의 파이프라인에서는 LAN의 처리 시간이 합쳐서 약 8ms(내부 측정, 나머지는 디스플레이 자체의 60Hz 주사율 주기)이고, 입력은 다음 프레임이 그려지기 전에 닿습니다. 원격 컴퓨터가 로컬과 구별되지 않습니다.
TURN 릴레이를 거치는 연결에서는 같은 지역이라 해도 네트워크 몫이 30~80밀리초로 뜁니다. 사용자가 체감하는 총 지연은 40~100ms가 됩니다. 대부분의 작업에는 여전히 쓸 만하지만 빠른 드래그나 연속 클릭에서는 느껴집니다. 트랙패드가 더는 내 것 같지 않습니다.
이 차이가 저희가 세션을 P2P에 두려고 그렇게 애쓰는 이유입니다. Remio의 WebRTC 스택은 IPv4와 IPv6 후보를 모두 모으고, 여러 STUN 서버와 병렬로 통신하며, ICE의 후보 쌍 선택에서 직접 경로를 적극 선호합니다. 내부적으로는 측정한 연결 RTT로 네트워크를 분류하기도 합니다. 15ms 미만이면 LAN임을 알고 인코더가 높게 시작해 훨씬 많은 대역폭을 쓸 수 있습니다. 그보다 느리면 WAN으로 보고 더 보수적으로 조정합니다.
그리고 경쟁 제품들이 흔히 조용히 빼놓는 부분인데, 연결은 경로와 무관하게 종단 간 암호화됩니다. 화면은 저희 서버를 거치지 않으며, TURN 릴레이도 암호문만 봅니다. 키는 픽셀이 움직이기 전, 핸드셰이크 중에 두 피어 사이에서 직접 협상되기 때문입니다. 바이트가 P2P로 흐르든 TURN을 거치든 Remio의 시그널링 서버도 TURN 운영자도 읽을 수 없습니다. P2P란 데이터 경로에 제3자가 아예 없다는 뜻일 뿐입니다.
릴레이가 불가피할 때 (그래도 괜찮습니다)
가끔 네트워크의 신이 안 된다고 합니다. 양쪽 모두 대칭 NAT, 엄격한 ISP의 모바일 통신사에서의 이중 CGNAT, 알려진 클라우드 제공업체로 향하는 아웃바운드 TLS만 허용하는 기업 방화벽은 실재하고 흔한 구성입니다. 그런 경우 세션은 릴레이를 거치거나 아예 성립하지 않습니다.
Remio의 TURN 백엔드는 Cloudflare Calls이며, 대부분의 사용자 가까이에 전 세계 릴레이 네트워크를 제공합니다. ICE가 릴레이 경로를 선택하면 경험은 "로컬처럼 느껴짐"에서 "Splashtop 같은 느낌"으로 내려갑니다. 여전히 충분히 쓸 만하지만 티가 납니다. 같은 지역 릴레이에서는 지연이 30~80ms 늘고, 지역을 가로지르면 200ms까지 갈 수 있습니다.
중요한 점은 데이터가 여전히 종단 간 암호화된다는 것입니다. Cloudflare는 한쪽으로 가는 암호화된 UDP 패킷과 반대쪽으로 가는 암호화된 UDP 패킷을 봅니다. 과금을 위해 바이트 수는 셀 수 있지만 화면도, 키 입력도, 세션 내용도 볼 수 없습니다. Remio 쪽에도 복호화 지점이 없습니다. 파이프라인에 Remio 클라우드가 없기 때문입니다. 저희는 Cloudflare를 통해 TURN 서비스를 운영할 뿐 페이로드는 건드리지 않습니다.
이것이 보안 백서가 더 자세히 설명하는 아키텍처입니다. 요약하면 P2P든 릴레이든 위협 모델은 같습니다.
IPv6라는 비상구
IPv6에는 340 undecillion개(3.4 × 1038)의 주소가 있습니다. 지구의 모래알 하나하나에 공인 IP를 주고도 수조 개가 남습니다. IPv6만 쓰는 세상에서 NAT는 대체로 불필요합니다. IPv6를 지원하는 두 기기는 서로의 주소로 패킷을 보내면 되고, 유일한 장애물은 양쪽의 호스트 방화벽입니다.
집과 모바일 통신사 모두 IPv6라면, 양쪽이 IPv6일 때 어떤 앱이 더 빨리 연결되는 것을 느꼈을 수 있습니다. "후보를 모으고 쌍을 탐색하는" 춤의 장애물이 적기 때문입니다. 발견할 NAT 변환도, STUN 왕복도 필요 없고 직접 주소만 있습니다.
하지만 함정이 있습니다. 2026년에도 전 세계 보급은 아직 완전하지 않습니다. 많은 가정용 공유기가 IPv6를 서툴게 처리하거나 아예 지원하지 않습니다. 많은 기업 네트워크는 기본적으로 인바운드 IPv6를 막습니다. 일부 통신사는 무선 구간에서는 IPv6를 제공하지만 다른 곳에서는 IPv4로 브리지합니다. 순수한 IPv6 대 IPv6 경로는 존재하지만 유일한 수단으로 기댈 수는 없습니다.
그래서 Remio는 모든 진지한 P2P 스택처럼 IPv4와 IPv6 후보를 모두 모으고 ICE가 경주를 시키게 합니다. 직접 IPv6 경로가 있고 동작하면 보통 지연에서 이깁니다. 아니면 홀 펀칭을 쓰는 IPv4가 이어받습니다. 둘 다 실패하면 릴레이입니다. 사용자는 이 중 아무것도 보지 못하고, 연결이 그냥 맺어집니다.
세션이 어떤 경로를 쓰는지 아는 방법
내 세션이 어떤 경로로 연결됐는지 궁금한가요? Remio의 연결 표시기는 유형을 쉬운 말로 보여 줍니다. P2P는 "직접", TURN은 "릴레이"입니다. 개발자 통계 패널은 지정된 쌍의 후보 유형(host, srflx, relay)과 측정된 RTT를 포함한 전체 WebRTC 상태를 보여 줍니다.
간단한 해석 몇 가지입니다.
- RTT 5ms 미만, 직접 — 호스트와 같은 LAN에 있습니다. 최상의 경우입니다. 처리 시간 약 8ms(나머지는 디스플레이 자체의 60Hz 주사율 주기)를 달성할 수 있습니다.
- RTT 5~15ms, 직접 — 같은 Wi-Fi 네트워크지만 액세스 포인트나 메시 단계가 하나 더 있습니다. 여전히 훌륭합니다.
- RTT 15~80ms, 직접 — 호스트와 다른 네트워크에 있고 홀 펀칭이 성공했습니다. 인터넷 P2P가 동작하고 있습니다.
- RTT 30~80ms, 릴레이(같은 지역) — 대칭 NAT나 통신사급 NAT가 홀 펀칭을 막았습니다. 가까운 Cloudflare TURN이 이어받았습니다.
- RTT 80~200ms, 릴레이 — 더 먼 지리적 거리를 거치는 TURN이거나, 나와 호스트가 매우 다른 지역에 있습니다.
집에서도 계속 릴레이로 전환된다면 흔한 원인은 대칭 NAT를 하는 ISP 모뎀, 집 회선의 IPv4 CGNAT(광 재판매 회선에서 생각보다 흔합니다), 인바운드 UDP를 막는 과도한 방화벽입니다. 원격 데스크톱 지연 줄이기 가이드가 흔한 범인들을 짚어 주고, 두 기기가 같은 네트워크에 있다면 직접 LAN 연결 페이지가 지름길입니다.
실제 환경의 성능 수치
종합하면 Remio 세션이 거칠 수 있는 여러 경로에서 프로덕션에서 측정한 지연 수치는 다음과 같습니다.
- 직접 LAN P2P: 네트워크 RTT 1~5ms. 종단 간(입력 이벤트에서 픽셀 갱신까지): 나머지 파이프라인이 협조할 때 처리 시간 약 8ms(나머지는 디스플레이 자체의 60Hz 주사율 주기).
- 단계가 더 있는 같은 네트워크 Wi-Fi: 네트워크 RTT 5~10ms.
- TURN 릴레이, 같은 지역: 네트워크 RTT 30~80ms.
- TURN 릴레이, 지역 간: 네트워크 RTT 80~200ms.
- 4G/5G 모바일, 홀 펀칭된 P2P: 네트워크 RTT 40~100ms.
이 수치는 Remio의 벤치마크와 일치하며, LAN 대 WAN 기준을 RTT 15ms로 잡은 이유를 설명합니다. 직접 경로를 릴레이(최소 30ms)나 모바일 경로(최소 40ms)와 깔끔하게 가르고, 오탐이 매우 적습니다.
NAT 순회는 프로토콜 하나하나는 단순하지만 조합이 까다로운 주제입니다. 대부분의 사용자는 ICE, STUN, TURN이 있다는 사실조차 알 필요가 없고, 어디서든 어디로든 빠르게 연결되는 앱만 경험할 겁니다. 프로토콜은 우리가 알아채지 못할 때 제 일을 하고 있는 겁니다. 알아챘을 때, 즉 세션이 한 박자 늦게 시작되거나 "직접"이 "릴레이"로 바뀌었을 때, 이제 머릿속에 모델이 있습니다.
Remio의 네이티브 파이프라인과 네트워크 지연 1~5ms를 처리 시간 약 8ms(나머지는 디스플레이 자체의 60Hz 주사율 주기)로 만드는 선택에 대해서는 네이티브가 중요한 이유를 보세요. 릴레이 경로의 위협 모델은 보안 백서에 있습니다. 아니면 기능을 둘러보고 Remio를 다운로드해 직접 느껴 보세요.