한양과학기술고등학교

에듀테크 프로젝트 대시보드

WebRTC & NAT 트래버설

WebRTC & NAT 트래버설

영상통화·화면공유·게임 P2P는 브라우저끼리 직접 데이터를 주고받는다. 그런데 우리 컴퓨터는 대부분 공유기(NAT) 뒤에 숨어 있어서 서로의 진짜 주소를 모른다. WebRTC는 STUN·TURN·ICE라는 3가지 도구로 이 "서로 못 찾는 문제"를 푼다.

NAT 뚫기 애니메이션

host — 내 랜카드 사설 IP srflx — STUN이 알려준 공인 IP:포트 relay — TURN 서버가 대신 중계
공유기 뒤의 두 브라우저 A·B가 서로 직접 연결을 시도합니다. 연결 시작을 눌러 보세요.

ICE 후보 수집 로그

아직 후보 없음…

연결 절차 (시그널링 → ICE)

  1. A가 Offer(SDP)를 만들어 시그널링 서버를 거쳐 B에게 전달
  2. B가 Answer(SDP)로 응답 — 코덱·암호키 협상 완료
  3. 양쪽이 host 후보(사설 IP)를 서로 교환
  4. 각자 STUN에 물어 공인 IP:포트(srflx 후보)를 알아내 교환
  5. 후보쌍을 짝지어 연결성 검사(STUN ping) — 뚫리는 경로 탐색
  6. 가장 좋은 경로 선택 → P2P 데이터 채널 개통 (실패 시 TURN relay)

STUN vs TURN

구분STUNTURN
역할 "내 공인 주소가 뭐야?"를 알려주는 거울 두 피어 사이 트래픽을 대신 중계하는 우체국
데이터 경로 연결만 도와주고 빠짐 → 이후 완전 P2P 모든 패킷이 TURN 서버를 경유 (P2P 아님)
비용/지연 거의 공짜, 지연 최소 서버 대역폭 많이 씀, 지연·비용 증가
언제 대부분의 가정용 NAT (원뿔형) 대칭형 NAT·엄격한 회사 방화벽 등 최후의 수단

ICE는 host → srflx → relay 순으로 더 싼 경로부터 시도한다. 그래서 통계상 약 8~15%의 연결만 TURN까지 내려간다. 서비스 입장에선 이 소수를 위해 TURN 서버를 꼭 준비해야 통화가 "가끔 안 되는" 일이 없다.

브라우저 코드 스케치

const pc = new RTCPeerConnection({
  iceServers: [
    { urls: "stun:stun.l.google.com:19302" },           // 공인 주소 거울
    { urls: "turn:turn.example.com",
      username: "u", credential: "p" }            // 중계 (fallback)
  ]
});

// ICE 후보가 하나씩 발견될 때마다 상대에게 전달 (Trickle ICE)
pc.onicecandidate = (e) => {
  if (e.candidate) signaling.send({ ice: e.candidate });
};

// 데이터 채널 = 브라우저끼리 직접 연결된 파이프
const chan = pc.createDataChannel("chat");
chan.onmessage = (e) => render(e.data);

const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
signaling.send({ sdp: offer });   // 시그널링 서버는 "소개"만, 데이터는 안 봄

시그널링 서버는 WebRTC 표준에 없다 — WebSocket이든 뭐든 자유. 두 피어가 SDP·ICE 후보를 "주고받게만" 해주면 되고, 연결이 뚫린 뒤엔 미디어/데이터가 서버를 거치지 않는다.