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 | 미지원 | 기본 지원 |
| 미디어 코덱 | 내장 (장점이자 단점) | 직접 구현 (자유도 높음) |
구조 다이어그램
동작 흐름
클라이언트가 서버의 UDP 포트로 Initial 패킷 전송 (TLS 1.3 ClientHello 포함)
서버가 Handshake 패킷으로 응답 — 암호화 협상 + 연결 수립이 1-RTT에 완료
재방문 시 0-RTT: 이전 세션 키로 첫 패킷부터 데이터 전송 가능
독립적 스트림 다중화: 비디오·오디오·입력을 별도 스트림으로 전송, 하나가 유실돼도 나머지 영향 없음
Connection ID 기반 연결 관리: WiFi → LTE 전환 시 IP가 바뀌어도 연결 유지 (Connection Migration)
내장 흐름 제어 + 혼잡 제어로 네트워크 상태에 적응적 전송
장점
- ✓ 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/커널이 아니면 성능 저하 가능