본문으로 건너뛰기
김신건의 로그

[Voice AI] WebRTC: PeerConnection, ICE, STUN/TURN, SFU

· 수정 · 📖 약 3분 · 1,113자/단어 #webrtc #p2p #sfu #voice #video #realtime
WebRTC, RTCPeerConnection, ICE, STUN, TURN, SFU, MCU, DataChannel, DTLS-SRTP, getUserMedia

정의

WebRTC = 브라우저 / 모바일 P2P 실시간 미디어 + 데이터 표준. 음성, 비디오, 데이터 채널. 2026 시점 voice agent + 영상 통화 + 화면 공유 의 표준.

동작 직관

핵심 컴포넌트

flowchart TB
    JS["JavaScript API"]
    JS --> PC[RTCPeerConnection]
    JS --> GU[getUserMedia]
    JS --> DC[RTCDataChannel]
    PC --> Media["미디어 트랙 (Audio/Video)"]
    PC --> Sig["Signaling (별도 채널)"]
    PC --> ICE[ICE candidates]
    ICE --> STUN[STUN server]
    ICE --> TURN["TURN server (relay)"]
    PC --> DTLS[DTLS handshake]
    PC --> SRTP["SRTP (암호화 미디어)"]
API역할
getUserMedia마이크 / 카메라 접근
RTCPeerConnectionP2P 연결
RTCDataChannel임의 데이터
RTCRtpSender/Receiver미디어 송수신 제어

Signaling (WebRTC 가 직접 다루지 않음)

sequenceDiagram
    autonumber
    participant A as Browser A
    participant Sig as Signaling Server<br/>(WebSocket)
    participant B as Browser B

    A->>A: createOffer (SDP)
    A->>Sig: send offer
    Sig->>B: relay offer
    B->>B: setRemoteDescription
    B->>B: createAnswer (SDP)
    B->>Sig: send answer
    Sig->>A: relay answer
    A->>A: setRemoteDescription

    par 양쪽 ICE candidates
        A->>Sig: ICE candidate
        Sig->>B: relay
        B->>Sig: ICE candidate
        Sig->>A: relay
    end

    A->>B: P2P 직접 연결 (DTLS + SRTP)

Signaling 은 WebRTC 표준 외부. WebSocket / HTTP 폴링 등 자유.

SDP (Session Description Protocol)

v=0
o=- 1719318060 2 IN IP4 127.0.0.1
s=-
t=0 0
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104
a=rtpmap:111 opus/48000/2
a=rtpmap:103 ISAC/16000
a=fmtp:111 minptime=10;useinbandfec=1
a=setup:actpass
a=fingerprint:sha-256 AC:8D:...
...

코덱 / 포맷 / 보안 / ICE 후보 등. offer/answer 모델.

ICE / STUN / TURN

flowchart LR
    A[Browser A] -->|"behind NAT"| Net[(Internet)]
    B[Browser B] -->|"behind NAT"| Net
    A -.STUN query.-> STUN[STUN server]
    STUN -.public IP/port.-> A
    A -.candidate exchange.-> B
    A -->|direct| B
    A -.failed?.-> TURN["TURN server (relay)"]
    TURN -->|relay| B
STUNTURN
역할내 공용 IP/Port 알려줌미디어 중계 (P2P 실패 시)
부하낮음높음 (모든 미디어 흐름)
비용거의 0비쌈 (대역폭)
stun:stun.l.google.com:19302coturn (self-host)

약 20% 의 사용자가 symmetric NAT / 방화벽 때문에 TURN 필요. coturn 운영 필수.

ICE Candidate 유형

ICE 는 여러 후보를 수집해 우선순위 순으로 연결 시도합니다.

유형설명
host로컬 NIC IP/port192.168.1.5:50001
srflx (Server Reflexive)STUN 으로 알아낸 공용 IP/port203.0.113.7:50001
prflx (Peer Reflexive)상대방이 받은 내 패킷에서 알아낸 주소NAT 연결 중 발견
relayTURN 서버 주소198.51.100.2:3478

우선순위: host > srflx > prflx > relay. Symmetric NAT 이면 host/srflx 모두 실패하고 relay 로 fallback.

ICE Restart

네트워크 변경 (wifi → LTE 전환 등) 시 기존 ICE 연결이 끊깁니다. pc.restartIce() 로 새 offer 를 만들어 ICE 를 재수집하면 연결을 유지할 수 있습니다.

async function handleNetworkChange() {
  pc.restartIce();
  const offer = await pc.createOffer({ iceRestart: true });
  await pc.setLocalDescription(offer);
  // signaling 으로 offer 전송
}

DataChannel (임의 데이터)

const dc = pc.createDataChannel('chat', { ordered: true });
dc.onopen = () => dc.send('hello');
dc.onmessage = (e) => console.log(e.data);
모드의미
ordered + reliableTCP 같은 (기본)
unordered + unreliableUDP 같은 (게임)
reliable + maxPacketLifeTime시간 한도

SFU (Selective Forwarding Unit)

P2P 는 N 명 → N×(N-1)/2 연결. 4명 = 6 연결, 10명 = 45 연결. 폭발.

flowchart TB
    subgraph P2P["P2P (mesh)"]
        A1[A] --- B1[B]
        A1 --- C1[C]
        A1 --- D1[D]
        B1 --- C1
        B1 --- D1
        C1 --- D1
    end
    subgraph SFU["SFU (server forwards)"]
        SFUS[SFU Server]
        A2[A] --> SFUS
        SFUS --> A2
        B2[B] --> SFUS
        SFUS --> B2
        C2[C] --> SFUS
        SFUS --> C2
        D2[D] --> SFUS
        SFUS --> D2
    end
P2P (mesh)SFUMCU
적합1:1 / 작은 그룹5-100 명TV/대규모
클라이언트 부하N-1 stream1 publish + 멀티 download1 stream
서버 비용0중간 (forwarding)높음 (transcoding)
일반 WebRTCLiveKit, mediasoup, JanusJitsi Videobridge

DTLS-SRTP (미디어 암호화)

WebRTC 는 모든 미디어를 DTLS-SRTP 로 암호화합니다. DTLS 로 키 교환 후 SRTP 로 미디어 패킷을 암호화.

  • DTLS: UDP 위의 TLS. 핸드셰이크에서 a=fingerprint:sha-256 ... (SDP 에서 교환된 인증서 지문) 검증
  • SRTP: 대칭키로 RTP 패킷의 payload 암호화 + HMAC-SHA1 무결성

브라우저에서 평문 WebRTC 는 표준상 불가. MITM 차단.

Simulcast / SVC (다중 해상도 스트림)

SFU 에서 화면 크기가 다른 구독자에게 서로 다른 품질을 주기 위한 기술.

  • Simulcast: 송신자가 같은 영상을 여러 해상도 (720p, 360p, 180p) 로 동시 인코딩해 전송. SFU 가 선택.
  • SVC (Scalable Video Coding): 레이어 구조로 인코딩, 하위 레이어 위에 상위 레이어를 쌓는 방식. 대역폭 효율 좋음 (예: AV1-SVC, H264-SVC).
flowchart TB
    Sender["발신자"]
    Sender -->|"720p stream"| SFU
    Sender -->|"360p stream"| SFU
    Sender -->|"180p stream"| SFU
    SFU -->|"720p"| ClientHD["고대역폭 수신자"]
    SFU -->|"360p"| ClientMD["중대역폭 수신자"]
    SFU -->|"180p"| ClientLD["저대역폭 수신자"]

수신자 대역폭/CPU 에 따라 SFU 가 실시간으로 레이어를 전환해줍니다.

Bandwidth Estimation (REMB / TWCC)

WebRTC 는 수신자의 네트워크 상태를 추정해 송신자가 비트레이트를 자동 조절합니다.

알고리즘방향방식
REMB (Receiver Estimated Max Bitrate)수신 → 송신수신자가 추정 최대 속도를 RTCP 로 전송
TWCC (Transport-Wide Congestion Control)수신 → 송신수신 타임스탬프를 모두 보고, 송신측에서 추정

TWCC 가 더 정확하고 최신 표준. GCC (Google Congestion Control) 알고리즘이 TWCC 피드백을 사용합니다.

음성 에이전트 + WebRTC

flowchart LR
    User[Browser/Mobile] -->|WebRTC| SFU[LiveKit SFU]
    SFU -->|미디어 stream| Agent["Voice Agent (Pipecat / LiveKit Agents)"]
    Agent -->|STT| LLM
    LLM -->|TTS audio| Agent
    Agent -->|미디어 stream| SFU
    SFU -->|WebRTC| User

자세한 건 pipecat-livekit.

WebRTC vs WebSocket vs HTTP/3 (voice)

항목WebRTCWebSocketHTTP/3
Latency최저 (UDP)중간 (TCP)중간
Packet lossFEC + loss concealmentTCP 재전송 (HoL)QUIC stream 별
Echo cancel내장 (browser)직접직접
NAT 통과ICETCP/HTTP 기본 OKUDP 차단 위험
코덱Opus, VP8/9, H264직접직접
사용voice / video일반차세대

voice agent = WebRTC 가 거의 정답. 단 방화벽 UDP 차단 환경 은 WebSocket fallback.

getStats() 로 품질 모니터링

setInterval(async () => {
  const stats = await pc.getStats();
  stats.forEach(stat => {
    if (stat.type === 'inbound-rtp' && stat.kind === 'audio') {
      console.log('jitter', stat.jitter);
      console.log('packetsLost', stat.packetsLost);
      console.log('roundTripTime', stat.roundTripTime);
    }
  });
}, 2000);

주요 지표:

지표기준 (음성 품질)
jitter< 30ms 이면 양호
packetsLost (비율)< 1% 이면 양호, > 5% 이면 품질 저하
roundTripTime< 150ms 이면 양방향 통화에 OK
framesDropped낮을수록 좋음 (영상)

흔한 함정

WARNING

  1. STUN 만 + TURN 없음 = 20% 사용자 연결 실패. coturn 운영 필수.
  2. 에코 (사용자 음성 마이크로 다시) = getUserMedia({ audio: { echoCancellation: true } }). AEC 필수.
  3. Signaling = WebSocket 권장 = HTTP long-polling 은 느리고 깨짐 잦음.
  4. mesh 4명+ 운영 = N² 폭발. SFU 로 반드시 전환.
  5. SDP 직접 편집 = browser 버전마다 다름. libwebrtc / SFU lib 활용.

관련 위키

이 글의 용어 (5개)
[Django] Channels: WebSocket, Consumer, Groupdjango
정의 Django Channels는 Django에 WebSocket, 비동기 프로토콜, 백그라운드 작업을 추가하는 공식 확장. 기본 Django는 HTTP만, Channels는 …
[Voice AI] 음성 에이전트 아키텍처: Cascading / Streaming / S2S + Latency Budgetai
정의 실시간 음성 AI 의 3가지 패턴. 각자 지연 vs 유연성 vs 비용 트레이드오프. 3 패턴 비교 | | Cascading | Streaming | S2S | |---|--…
[Voice AI] Pipecat vs LiveKit Agents: 프레임워크 비교ai
정의 2026 시점 voice agent 프레임워크 4가지 선택지: | | Pipecat | LiveKit Agents | Vapi / Retell | 자체 구축 | |---|-…
TLS / SSLnetwork
정의 TLS (Transport Layer Security) 는 (또는 위 ) 위에서 동작하는 암호화·인증 프로토콜이다. SSL 은 TLS 의 전신 (Netscape 1995, …
UDPnetwork
정의 UDP (User Datagram Protocol)는 비연결·비신뢰 전송 계층 프로토콜이다. RFC 768 (1980) 로 정의되었다. TCP 와 대비된다. "보내고 잊는다…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기