작성일 :

네트워크 변화에 적응하는 기술

Part 2에서 WebRTC가 시그널링, ICE, DTLS-SRTP를 통해 P2P 연결을 수립하는 과정을 살펴보았습니다.

연결이 수립되면 미디어가 흐르기 시작합니다. 그러나 네트워크는 고정된 환경이 아닙니다. 사용자가 Wi-Fi에서 LTE로 전환하고, 다른 트래픽과 대역폭을 공유하며, 라우터의 큐가 차고 비워지기를 반복합니다.

초기 VoIP(Voice over IP) 시스템, 즉 인터넷을 통해 음성을 전달하는 기술은 네트워크 상태가 좋을 때만 제대로 동작했습니다. 대역폭이 줄어들면 통화 품질이 갑자기 떨어졌고, 패킷 손실이 발생하면 음성이 끊기거나 깨졌습니다. 2000년대 초반 Skype가 대중적으로 사용되기 시작하면서, 다양한 네트워크 환경에서도 안정적인 품질을 유지하는 기술이 필수가 되었습니다.

이러한 네트워크 변화는 세 가지 형태로 나타납니다. 패킷 도착 시간이 들쭉날쭉해지는 Jitter, 패킷이 아예 도달하지 않는 패킷 손실, 그리고 사용할 수 있는 가용 대역폭의 변동입니다. Part 1에서 RTP가 타이밍과 순서 정보를 제공하는 프레임워크임을 살펴보았지만, 이 세 가지 문제에 대한 구체적 대응은 애플리케이션의 몫으로 남겨져 있었습니다.

이 글에서는 WebRTC가 네트워크 변화에 대응하는 기법들을 살펴봅니다. Jitter Buffer, FEC, 적응형 비트레이트, 에코 캔슬레이션, 그리고 다자간 통화를 위한 SFU/MCU 아키텍처를 다룹니다.


Jitter Buffer

네트워크 지연은 일정하지 않습니다. 패킷 도착 시간 간격이 들쭉날쭉해지면, 재생 시점에 필요한 패킷이 아직 도착하지 않아 화면과 소리가 끊깁니다. Jitter Buffer는 패킷을 즉시 재생하지 않고 잠시 보관했다가 일정한 간격으로 꺼내 사용하여, 이 변동(Jitter)을 흡수합니다.

1
2
3
4
5
6
7
8
9
10
11
네트워크에서:
패킷 1 도착: 0ms
패킷 2 도착: 25ms (+5ms 지터)
패킷 3 도착: 35ms (-5ms 지터)
패킷 4 도착: 65ms (+5ms 지터)

직접 재생하면:
프레임 간격이 불규칙 → 끊김

Jitter Buffer 사용:
버퍼에서 일정 간격으로 꺼냄 → 부드러운 재생

버퍼 크기 트레이드오프

버퍼 크기는 지연과 안정성 사이의 트레이드오프입니다.

  작은 버퍼 큰 버퍼
지연 낮음 높음
Jitter 흡수 약함 강함
위험 재생 끊김 대화 지연 증가

고정된 버퍼 크기는 모든 네트워크 상황에 대응할 수 없습니다. 안정적인 네트워크에서 큰 버퍼를 사용하면 불필요한 지연이 발생하고, 불안정한 네트워크에서 작은 버퍼를 사용하면 재생이 자주 끊깁니다.


적응형 Jitter Buffer

적응형 Jitter Buffer는 네트워크의 Jitter를 실시간으로 측정하여 버퍼 크기를 동적으로 조절합니다. Jitter가 낮으면 버퍼를 줄여 지연을 낮추고, Jitter가 높으면 버퍼를 늘려 안정성을 확보합니다.

1
2
3
4
5
6
7
시간 경과 →

네트워크 상태:  안정적        불안정해짐       다시 안정적
Jitter:        ±2ms          ±15ms           ±3ms

버퍼 크기:     20ms ──────► 60ms ──────► 25ms
               (지연 낮음)   (안정성 확보)  (지연 다시 낮춤)

패킷 손실 대응

Jitter Buffer가 패킷 도착 시간의 변동을 흡수한다면, 패킷이 아예 도달하지 않는 손실 문제는 별도의 기법이 필요합니다. 실시간 통신에서는 재전송을 기다릴 시간이 부족하므로, 여러 복구 기법을 함께 사용합니다.

FEC (Forward Error Correction)

순방향 오류 수정은 원본 데이터와 함께 복구용 데이터를 미리 전송하여, 수신 측에서 손실된 패킷을 스스로 복원하는 기법입니다. 손실이 발생해도 송신 측에 재전송을 요청할 필요가 없으므로, RTT(왕복 지연)만큼의 대기 시간 없이 즉시 복구할 수 있습니다.

FEC의 복구 원리는 XOR(배타적 논리합) 연산에 기반합니다. XOR은 두 비트가 다르면 1, 같으면 0을 출력하는 연산입니다. 핵심 성질은 역연산이 가능하다는 점입니다. A ⊕ B = C이면, A ⊕ C = B가 성립합니다.

이 성질을 활용하여, P1과 P2를 XOR한 값을 FEC 패킷 F1으로 함께 전송합니다. P2가 손실되더라도 P1과 F1을 XOR하면 P2를 복원할 수 있습니다.

1
2
3
4
5
원본 패킷:  P1  P2  P3  P4
FEC 패킷:   F1 (P1⊕P2)  F2 (P3⊕P4)

P2 손실 시:
P2 = P1 ⊕ F1 로 복구

다만 FEC에는 트레이드오프가 있습니다. 위 다이어그램에서 원본 패킷 4개(P1~P4)를 보내면서 FEC 패킷 2개(F1, F2)를 추가로 전송하므로, 총 전송량이 50% 증가합니다. FEC 그룹 크기를 늘려 원본 5개당 FEC 1개로 줄이면 오버헤드는 20%로 낮아지지만, 복구 능력도 약해집니다. 이처럼 복구 패킷이 원본 대비 약 20~50%의 추가 대역폭을 소모하며, 같은 FEC 그룹 내에서 2개 이상의 패킷이 동시에 손실되면 복구가 불가능합니다. WebRTC는 네트워크 상태에 따라 FEC 비율을 동적으로 조절하여 이 오버헤드를 관리합니다.

WebRTC의 오디오 코덱인 Opus는 InBand FEC를 지원합니다. InBand FEC는 별도의 FEC 패킷을 전송하지 않고, 다음 패킷 안에 이전 패킷의 저품질 사본을 함께 담는 방식입니다. 이전 패킷이 손실되면 다음 패킷에 포함된 사본으로 복구합니다. SDP(세션 기술 프로토콜)에서 다음 한 줄로 활성화됩니다.

1
a=fmtp:111 useinbandfec=1

NACK과 재전송

FEC가 복구 정보를 미리 전송하는 방식이라면, NACK(Negative Acknowledgment)은 손실을 감지한 뒤 재전송을 요청하는 방식입니다. 수신자가 시퀀스 번호를 확인하여 누락된 패킷을 발견하면, 즉시 송신자에게 해당 패킷의 재전송을 요청합니다.

1
2
3
4
5
6
7
8
수신자                         송신자
  │                              │
  │   패킷 1,2,4 수신 (3 손실)    │
  │                              │
  │ ── RTCP NACK (seq=3) ──────► │
  │                              │
  │ ◄──── 패킷 3 재전송 ──────── │
  │                              │

NACK은 RTT가 낮을 때만 효과적입니다. RTT가 높으면 재전송된 패킷이 재생 시점을 놓치기 때문입니다. FEC로 복구할 수 없고, NACK 재전송도 시간에 맞지 않는 경우에는 마지막 수단이 필요합니다.


Packet Concealment (은닉)

FEC와 NACK으로도 복구가 불가능한 상황에서, 은닉(concealment) 기법은 이전 데이터를 기반으로 빈 자리를 메웁니다. 사람의 청각과 시각은 짧은 결함에 관대하므로, 완벽한 복구 대신 눈치채기 어려운 수준의 보정을 목표로 합니다.

오디오에서는 직전 샘플을 짧게 반복하여 무음을 피하거나, 이전과 이후 샘플을 선형 보간(Interpolation)하여 자연스러운 전환을 만듭니다. 손실 구간이 수 밀리초 이내로 매우 짧으면, 보간 연산 없이 짧은 무음(silence)을 삽입하는 것만으로도 청취자가 알아채기 어렵습니다.

비디오에서는 직전 프레임을 유지하는 방법이 가장 단순합니다. 더 정교한 방식으로는 모션 보상 예측이 있는데, 이전 프레임의 움직임 벡터를 활용하여 손실된 프레임의 내용을 추정합니다.


적응형 비트레이트 (ABR)

앞에서 살펴본 Jitter Buffer, FEC, NACK은 이미 발생한 문제를 사후에 보정하는 기법입니다. 하지만 네트워크 대역폭 자체가 줄어들면, 보정만으로는 한계가 있습니다. 적응형 비트레이트(ABR)는 문제가 발생하기 전에 전송량 자체를 조절하여, 끊김 없는 재생을 유지합니다.

대역폭 추정

송신자는 자신이 보낸 패킷이 상대방에게 잘 도착했는지 직접 알 수 없습니다. 수신자가 알려줘야 합니다. Part 1에서 살펴본 RTCP가 이 역할을 합니다. 수신자는 RTCP Receiver Report를 통해 패킷 손실률과 Jitter 정보를 송신자에게 주기적으로 보고합니다. 손실률이 높아지거나 Jitter가 커지면 네트워크가 혼잡하다는 신호이므로, 송신자는 가용 대역폭이 줄었다고 판단합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
송신자                              수신자
  │                                   │
  │ ── RTP 패킷들 (2 Mbps) ────────►  │
  │                                   │
  │ ◄── RTCP Receiver Report ──────── │
  │     (손실률 8%, Jitter 45ms)      │
  │                                   │
  │  혼잡 감지 → 인코더 비트레이트 낮춤  │
  │                                   │
  │ ── RTP 패킷들 (500 Kbps) ───────► │
  │                                   │
  │ ◄── RTCP Receiver Report ──────── │
  │     (손실률 0.5%, Jitter 8ms)     │

WebRTC는 GCC(Google Congestion Control)를 사용하여 가용 대역폭을 추정합니다. GCC는 두 가지 신호를 결합합니다. 손실 기반(loss-based) 추정은 패킷 손실률이 높아지면 네트워크가 혼잡하다고 판단하여 전송 속도를 낮춥니다. 지연 기반(delay-based) 추정은 패킷 도착 간격이 점점 늘어나는 것을 감지하여, 손실이 발생하기 전에 미리 혼잡을 예측합니다. 두 추정치 중 더 낮은 값을 실제 전송 속도로 채택하여, 네트워크 과부하를 방지합니다.

인코더 조절

대역폭이 부족하면 비트레이트를 낮추고, 여유가 있으면 높입니다. 비트레이트만 낮추면 같은 해상도에서 압축률이 높아져 화면이 깨지므로, 해상도와 프레임레이트도 함께 낮춥니다. 예를 들어 대역폭이 충분할 때는 1280×720 해상도에 30fps, 2 Mbps로 전송하지만, 대역폭이 줄어들면 640×360 해상도에 15fps, 500 Kbps로 낮춰서 화질은 떨어지더라도 끊김 없는 재생을 유지합니다.

Simulcast

앞서 다룬 인코더 조절 방식은 송신자가 하나의 품질만 전송합니다. 수신자가 한 명이면 충분하지만, 여러 수신자의 네트워크 상태가 서로 다르면 모두를 만족시킬 수 없습니다. 한 수신자는 고속 Wi-Fi로 연결되어 있고, 다른 수신자는 느린 LTE로 연결되어 있다면, 하나의 비트레이트로는 둘 중 하나가 불만족스러운 품질을 받게 됩니다.

Simulcast는 이 문제를 해결하기 위해, 송신자가 동일한 영상을 여러 해상도와 비트레이트로 인코딩하여 동시에 전송합니다. 서버(SFU)는 각 수신자의 네트워크 상황에 맞는 스트림을 선택하여 전달합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
                    송신자
     ┌────────┬────────┬────────┐
     │ High   │ Medium │ Low    │
     │ 720p   │ 360p   │ 180p   │
     └───┬────┴───┬────┴───┬────┘
         │        │        │
         ▼        ▼        ▼
    ┌────────────────────────────┐
    │       SFU (선택 전달)       │
    └──┬─────────────────────┬───┘
       │                     │
       │ High (720p)         │ Low (180p)
       ▼                     ▼
  수신자 A                수신자 B
  (Wi-Fi, 5 Mbps)        (LTE, 300 Kbps)

수신자의 네트워크 상태가 변하면, SFU는 전달하는 스트림을 실시간으로 전환합니다. 대역폭이 줄어든 수신자에게는 High 대신 Low 스트림을 보내고, 대역폭이 회복되면 다시 High로 올립니다.

SVC (Scalable Video Coding)

Simulcast는 여러 해상도의 스트림을 독립적으로 인코딩합니다. 저해상도 스트림의 픽셀 수가 적어 CPU 오버헤드 자체는 약 1.3~1.5배 수준이지만, 각 스트림이 서로 독립적이므로 중복되는 정보를 공유하지 못합니다. SVC(Scalable Video Coding)는 이 중복을 제거하는 접근입니다. 하나의 스트림을 계층 구조로 인코딩하여, 수신자가 필요한 계층만 선택적으로 수신할 수 있게 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
              송신자 (하나의 SVC 스트림)
     ┌──────────────────────────────────┐
     │  Enhancement 2 (High)           │
     ├──────────────────────────────────┤
     │  Enhancement 1 (Medium)          │
     ├──────────────────────────────────┤
     │  Base Layer (Low)                │
     └───────────────┬──────────────────┘
                     │
                     ▼
            ┌─────────────────┐
            │  SFU (레이어 선택) │
            └──┬──────────┬───┘
               │          │
               │          │ Base만
               │          ▼
               │     수신자 B (LTE)
               │
               │ Base + Enh1 + Enh2
               ▼
          수신자 A (Wi-Fi)

Base Layer는 모든 수신자가 재생할 수 있는 최소 정보를 담고, Enhancement Layer가 추가될수록 화질이 높아집니다. 대역폭이 넉넉한 수신자는 모든 레이어를 수신하고, 대역폭이 제한된 수신자는 Base Layer만 수신하거나 Base + Enhancement 1까지만 수신합니다. SFU는 수신자의 상태에 따라 전달할 레이어를 선택합니다.


에코 캔슬레이션

앞에서 살펴본 기법들은 모두 네트워크 경로에서 발생하는 문제를 다루었습니다. 그러나 실시간 음성 통화에서는 네트워크와 무관한 또 다른 품질 저하 요인이 있습니다. 스피커 출력이 마이크로 되돌아오는 에코입니다.

음향 에코의 원인

화상 회의나 VoIP 통화에서 스피커를 사용하면, 상대방의 목소리가 스피커를 통해 나온 뒤 다시 마이크로 들어가는 현상이 발생합니다. 이를 음향 에코(Acoustic Echo)라고 하며, 상대방은 자신의 목소리가 지연되어 되돌아오는 것을 듣게 됩니다. 이어폰을 사용하면 에코가 크게 줄지만, 스피커폰이나 회의실 환경에서는 에코가 필연적으로 발생합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
     사용자 A                    네트워크                   사용자 B
  ┌───────────┐                                        ┌───────────┐
  │           │  ①  A의 목소리                          │           │
  │  마이크   │ ─────────────────────────────────────►  │  스피커   │
  │           │                                        │     │     │
  │           │                                        │     │ ②  │
  │           │                                        │  음향반사  │
  │           │                                        │     │     │
  │           │  ③  A의 목소리가 되돌아옴                │     ▼     │
  │  스피커   │ ◄─────────────────────────────────────  │  마이크   │
  │           │                                        │           │
  └───────────┘                                        └───────────┘
  A는 자신의 목소리가
  지연되어 들림 (에코)

AEC (Acoustic Echo Cancellation)

WebRTC는 소프트웨어 기반 AEC를 기본으로 제공합니다. AEC(Acoustic Echo Cancellation)는 스피커로 출력한 신호를 미리 알고 있으므로, 마이크 입력에서 해당 신호의 반향을 찾아내어 제거하는 방식으로 동작합니다.

1
2
3
4
5
6
7
8
9
10
스피커 출력 (상대방 목소리)
    │
    ├──────────────────────────► 에코 추정 모델에 기억
    │                                    │
    ▼                                    │ 추정된 에코
  [방 안에서 반사]                        │
    │                                    ▼
    ▼                              ┌───────────┐
  마이크 ──► [내 목소리 + 에코] ──► │ 에코 빼기  │ ──► 내 목소리만 전송
                                   └───────────┘

네트워크 문제 진단

앞에서 살펴본 품질 관리 기법들이 동작하더라도, 실제 통화에서 문제가 발생하면 원인을 파악해야 합니다.

getStats() API

WebRTC는 연결 품질 모니터링을 위해 getStats() API를 제공합니다. 패킷 손실, Jitter, 프레임 드롭 등 다양한 지표를 실시간으로 반환합니다.

1
2
3
4
5
6
7
8
9
10
11
const stats = await peerConnection.getStats();

stats.forEach(report => {
  if (report.type === 'inbound-rtp' && report.kind === 'video') {
    console.log('Packets received:', report.packetsReceived);
    console.log('Packets lost:', report.packetsLost);
    console.log('Jitter:', report.jitter);
    console.log('Frames decoded:', report.framesDecoded);
    console.log('Frames dropped:', report.framesDropped);
  }
});

품질 지표 해석

패킷 손실률 - 전송 패킷 중 수신자에게 도달하지 못한 비율입니다. 1% 미만이면 우수하고, 1-5%에서는 FEC나 은닉 기법이 활성화되며, 5% 이상이면 비디오 정지나 오디오 끊김이 발생합니다.

Jitter - 패킷 도착 시간의 변동입니다. 30ms 미만이면 우수하고, 30-50ms에서는 Jitter Buffer 크기가 증가하며, 50ms 이상이면 버퍼가 불안정해집니다.

RTT - 패킷 왕복 시간입니다. 150ms 미만이면 대화에 적합하며, 300ms 이상이면 말이 겹치는 현상이 자주 발생합니다.

일반적인 문제들

getStats() 지표와 아래 패턴을 대조하면 문제를 빠르게 진단할 수 있습니다.

  • 비디오 정지 — 높은 패킷 손실, 키프레임(I-frame) 손실, 대역폭 부족. 키프레임은 전체 화면 정보를 담은 기준 프레임이며, 이후 프레임은 키프레임과의 차이만 저장합니다. 키프레임이 손실되면 후속 프레임을 디코딩할 수 없으므로 화면이 정지합니다.
  • 오디오 끊김 — Jitter 증가, 버퍼 언더런(buffer underrun), CPU 과부하. 버퍼 언더런은 재생 시점에 버퍼가 비어서 출력할 데이터가 없는 상태입니다.
  • 단방향 오디오(한쪽만 들림) — NAT 트래버설 실패, 방화벽 차단, ICE 후보 교환 문제

MCU vs SFU

지금까지 살펴본 품질 관리 기법은 주로 1:1 통화를 기준으로 설명했습니다. 다자간 화상 회의에서는 여러 참가자의 미디어를 효율적으로 전달하기 위한 별도의 서버 아키텍처가 필요합니다.

각 참가자가 다른 모든 참가자의 스트림을 개별적으로 받으면, 클라이언트의 대역폭과 CPU 부담이 참가자 수에 비례하여 증가합니다. 10명이 참가한 회의라면 각 클라이언트가 9개의 스트림을 동시에 수신하고 디코딩해야 합니다. 이 문제를 해결하는 두 가지 주요 방식이 MCU(Multipoint Control Unit)SFU(Selective Forwarding Unit)입니다.

MCU는 서버에서 모든 스트림을 디코딩하고 하나의 화면으로 합성하여 재인코딩한 뒤, 각 참가자에게 단일 스트림으로 전달합니다.

SFU는 미디어를 디코딩하거나 믹싱하지 않고, 패킷을 그대로 필요한 수신자에게 선택적으로 전달만 합니다.

1
2
3
4
5
6
7
8
MCU — 서버가 합성하여 하나의 스트림으로 전달

  A ──┐                              ┌──► A : 합성 화면 1개
  B ──┤   ┌──────────────────────┐   ├──► B : 합성 화면 1개
  C ──┼──►│ 디코딩 → 믹싱 → 재인코딩 │──►┼──► C : 합성 화면 1개
  D ──┤   └──────────────────────┘   ├──► D : 합성 화면 1개
      └                              └
  서버 CPU: 높음 (인코딩/디코딩)     클라이언트 부담: 낮음 (1개만 수신)
1
2
3
4
5
6
7
8
SFU — 서버가 패킷을 그대로 전달

  A ──┐                              ┌──► A : B, C, D 각각 3개 스트림
  B ──┤   ┌──────────────────────┐   ├──► B : A, C, D 각각 3개 스트림
  C ──┼──►│ 라우팅만 (디코딩 없음)   │──►┼──► C : A, B, D 각각 3개 스트림
  D ──┤   └──────────────────────┘   ├──► D : A, B, C 각각 3개 스트림
      └                              └
  서버 CPU: 낮음 (전달만)            클라이언트 부담: 높음 (N-1개 수신)

MCU는 서버가 모든 미디어를 디코딩하고 믹싱하여 재인코딩하므로 서버 CPU 사용량이 높지만, 클라이언트는 참가자 수와 관계없이 하나의 스트림만 수신합니다.

SFU는 패킷을 디코딩 없이 그대로 전달하므로 서버 부담이 낮지만, 각 클라이언트가 N-1개의 스트림을 개별적으로 수신하고 디코딩해야 합니다.

현재 대부분의 화상 회의 서비스(Google Meet, Zoom 등)가 SFU 기반 아키텍처를 채택하고 있으며, Simulcast와 결합하여 각 수신자에게 적절한 품질의 스트림을 전달합니다.

확장성 트레이드오프

참가자 수가 증가하면 이 차이가 더 뚜렷해집니다. 10명이 참가한 회의에서 MCU는 10개 스트림을 디코딩하고 10개의 합성 화면을 재인코딩해야 하므로 서버가 총 20회의 인코딩/디코딩을 수행합니다. SFU는 디코딩 없이 패킷을 전달만 하므로 서버 부담은 거의 늘지 않지만, 각 클라이언트가 9개의 스트림을 개별 수신하고 디코딩해야 합니다.

50명 규모의 회의에서는 MCU 서버 부하가 감당하기 어려운 수준이 되는 반면, SFU 서버는 여전히 합리적으로 유지됩니다. 다만 SFU 클라이언트가 49개의 스트림을 개별 수신하는 것도 현실적이지 않으므로, 대규모 회의에서는 Simulcast + SFU 조합으로 수신자별 품질을 조절하거나, 발언자의 스트림만 선택적으로 전달하는 방식을 사용합니다.


마무리

  • Jitter Buffer는 패킷 도착 시간의 변동을 흡수하고, FEC와 NACK은 손실된 패킷을 복구합니다.
  • 적응형 비트레이트는 가용 대역폭에 맞춰 인코딩을 조절하며, AEC는 스피커 출력이 마이크로 되돌아오는 에코를 제거합니다.
  • 다자간 통화에서는 SFU + Simulcast 조합이 서버 부하와 클라이언트 대역폭 사이의 균형을 잡습니다.

Part 1의 RTP가 미디어 전송 틀을, Part 2의 시그널링이 통신 경로를 만들었다면, 이번 Part 3의 품질 관리 기법들은 그 경로 위에서 변화하는 네트워크 조건에 실시간으로 적응하는 역할을 합니다. 각 계층이 맡은 역할이 맞물려야 실제 사용 가능한 통화 품질이 만들어집니다.



관련 글

시리즈

Tags: ABR, FEC, MCU, QoS, SFU, WebRTC, 네트워크

Categories: ,