🚀

QUIC vs WebRTC: 왜 QUIC으로 돌아왔나?

WebRTC의 복잡함에 지쳐 QUIC으로 전환한 실전 사례

QUIC은 Google이 설계하고 HTTP/3의 기반이 된 전송 프로토콜이다. TCP의 3가지 고질적 문제 — 느린 연결 수립(2~3 RTT), Head-of-Line Blocking(패킷 하나 유실 시 전체 대기), IP 변경 시 연결 끊김 — 를 UDP 위에서 해결했다.

Noctiluca 개발기 — 왜 QUIC에 도달했나

unstabler 라는 개발자가 GeekNews에 올린 실전 개발기가 이 글의 출발점이다. macOS가 싫어서 Linux 데스크톱을 고집하다가, iOS 앱 개발 비중이 커지면서 원격 제어 소프트웨어를 직접 만든 이야기.

시도 1: xrdp + ulalaca

RDP(Remote Desktop Protocol)의 오픈 소스 구현체 xrdp를 macOS에 붙이려 했다. VNC 백엔드 + ScreenCaptureKit 을 조합한 'ulalaca'라는 xrdp 플러그인을 만들었지만, 실사용 수준에 도달하지 못했다.

문제:

  • Windows 최신 버전에서 GFX(H.264) / RemoteFX 코덱 지원이 사라짐

  • 화면 표시 외 기능(소리, 클립보드 동기화) 개발·디버깅 난이도가 극악

시도 2: WebRTC

ulalaca를 포기하고 반년 뒤, Noctiluca를 구상하면서 WebRTC로 재도전. 하지만 WebRTC도 만만치 않았다.

  • 커스터마이징의 벽: 화면 데이터를 비디오 소스로 쓰려면 Google Chromium 소스를 받아서 수정 → 빌드를 반복해야 했다. 코덱 파라미터 하나 바꿨더니 하드웨어 인코딩이 죽었고, 원인 찾으려고 소스 헤집고 로그 추가하고 다시 빌드

  • 포트 고정 불가: 시그널링 서버, TURN/STUN 서버가 필요하고, 나가는 포트를 고정할 수 없고 포트 재사용도 불가능

  • SCTP의 저주: WebRTC DataChannel은 내부적으로 SCTP를 사용하는데, 한 메시지의 페이로드가 MTU를 넘으면 비디오/오디오 스트림에 렉이 걸리기 시작

시도 3: QUIC — 드디어 해결

WebRTC에 지쳐 또 반년 방치. 카페에서 멍 때리다가 HTTP/3의 기반인 QUIC이 떠올랐다. macOS/iOS의 Network.framework에서 QUIC 구현체를 제공하고 있어서 프로토타입을 빠르게 만들었는데, WebRTC의 모든 문제가 즉시 해결됐다.

HOL Blocking 해결: TCP나 SCTP 기반에서는 패킷 하나가 유실되면 뒤따르는 모든 데이터가 멈춘다. QUIC은 스트림이 독립적이라 오디오 패킷 하나가 튀어도 마우스 입력이나 비디오 프레임은 계속 흘러온다.

단일 UDP 포트: WebRTC처럼 시그널링 서버니 STUN/TURN이니 복잡하게 구성할 필요가 없다. UDP 포트 하나만 열면 끝. WiFi AP를 전환해도 Connection Migration 덕분에 연결이 유지.

Noctiluca의 현재

QUIC 기반의 자체 설계 프로토콜 'Sirius'를 사용한다. H.264/HEVC 지원, HDR 전송(실험적), PAM/SSH 키 인증, 플러그인 확장 구조. 현재 iOS/macOS 클라이언트만 있고, Qt/C++ 기반 Linux/Windows 클라이언트를 개발 중.

개발자가 부산물로 만든 것들:

  • swift-msquic: iOS/macOS에서 MsQuic을 Swift로 쓸 수 있는 wrapper

  • Xuanxue: Swift에서 SSH 키 서명/검증 모듈

커뮤니티 반응 — Parsec과의 비교

"Parsec 쓰면 되지 않나?" 라는 반응이 있었다. Parsec은 실제로 최고 수준의 원격 접속 툴이지만, 모니터 사이즈가 같아야 하는 제약이 있고 모바일(iPad)을 지원하지 않는다. Noctiluca가 노리는 건 Parsec이 안 되는 영역 — RemoteApp 같은 개별 창 표시, USB 리디렉션(ThinkPad에 아이폰을 꽂으면 원격 Mac에서 인식), 그리고 궁극적으로 Linux 랩탑에서 iOS 개발.

QUIC이 원격 제어에 적합한 이유 정리

문제 WebRTC QUIC
인프라 복잡도 시그널링+STUN+TURN UDP 포트 1개
커스터마이징 Chromium 소스 수정·빌드 표준 라이브러리 사용
HOL Blocking SCTP DataChannel에서 발생 스트림 독립으로 해결
포트 고정 불가 가능
Connection Migration 미지원 기본 지원
미디어 코덱 내장 (장점이자 단점) 직접 구현 (자유도 높음)

구조 다이어그램

연결 수립 비교: TCP+TLS vs QUIC
TCP + TLS (기존)
RTT 1 TCP SYN → SYN-ACK → ACK
RTT 2 TLS ClientHello → ServerHello
RTT 3 TLS Finished → 데이터 전송 시작
2~3 RTT 소요
QUIC
RTT 1 Initial (TLS 1.3 포함) → 연결 완료!
재방문 0-RTT: 첫 패킷부터 데이터 전송!
1 RTT (재방문 0-RTT)
Head-of-Line Blocking 문제
TCP / WebRTC SCTP
Video Audio Input BLOCKED
패킷 1개 유실 → 전부 대기
QUIC (독립 스트림)
Stream 1: Video x 유실 이것만 재전송
Stream 2: Audio ✓ 계속 전송
Stream 3: Input ✓ 계속 전송
인프라 복잡도: WebRTC vs QUIC
WebRTC
필수 시그널링 서버 (SDP 교환)
필수 STUN 서버 (공인 IP 확인)
필수 TURN 서버 (NAT 뒤 중계)
문제 포트 고정 불가 / 재사용 불가
문제 Chromium 소스 수정 필요 (커스텀)
QUIC
UDP 포트 1개만 열면 됨
해결 시그널링/STUN/TURN 불필요
해결 포트 고정 가능
해결 Network.framework 등 네이티브 지원
Connection Migration
📶
WiFi AP-A
IP: 192.168.1.10
AP 전환
📶
WiFi AP-B
IP: 192.168.2.20
TCP/WebRTC
IP 변경 → 연결 끊김
QUIC
Connection ID 유지 → 끊김 없음
실전 교훈: WebRTC는 브라우저 P2P 화상통화에 최적화된 도구. 원격 데스크톱처럼 커스텀이 필요한 실시간 스트리밍에는 QUIC이 훨씬 단순하고 강력

동작 흐름

1

클라이언트가 서버의 UDP 포트로 Initial 패킷 전송 (TLS 1.3 ClientHello 포함)

2

서버가 Handshake 패킷으로 응답 — 암호화 협상 + 연결 수립이 1-RTT에 완료

3

재방문 시 0-RTT: 이전 세션 키로 첫 패킷부터 데이터 전송 가능

4

독립적 스트림 다중화: 비디오·오디오·입력을 별도 스트림으로 전송, 하나가 유실돼도 나머지 영향 없음

5

Connection ID 기반 연결 관리: WiFi → LTE 전환 시 IP가 바뀌어도 연결 유지 (Connection Migration)

6

내장 흐름 제어 + 혼잡 제어로 네트워크 상태에 적응적 전송

장점

  • 0-RTT / 1-RTT 연결 수립 (TCP+TLS 대비 1~2 RTT 절약)
  • Head-of-Line Blocking 완전 해결 (스트림 독립)
  • Connection Migration (IP 바뀌어도 연결 유지)
  • UDP 포트 하나로 모든 통신 (WebRTC처럼 복잡한 인프라 불필요)
  • TLS 1.3 내장 (별도 암호화 레이어 불필요)

단점

  • UDP 기반이라 일부 방화벽/네트워크에서 차단 가능
  • 기존 TCP 미들박스(방화벽, IDS)와 호환성 문제
  • 구현 복잡도 높음 (자체 재전송, 혼잡 제어 구현 필요)
  • WebRTC처럼 미디어 코덱/에코 제거 등은 미내장 (직접 구현)
  • UDP 최적화된 NIC/커널이 아니면 성능 저하 가능

사용 사례

HTTP/3 (모든 최신 웹 브라우징) 원격 데스크톱 / 화면 공유 (Noctiluca) 실시간 게임 (저지연 UDP + 신뢰성) 모바일 앱 API (네트워크 전환 빈번) CDN / 엣지 서버 간 통신