[Frontend_Roadmap] HTTP/3 A to Z 알아보기 1부: HTTP/3 역사 및 핵심 개념
오늘은 HTTP/3를 파헤쳐보는 시간을 가져보도록 하겠습니다.
https://www.smashingmagazine.com/2021/08/http3-core-concepts-part1/#top
HTTP/3 From A To Z: Core Concepts — Smashing Magazine
What exactly is HTTP/3? Why was it needed so soon after HTTP/2 (which was only finalized in 2015)? How can or should you use it? And especially, how does this improve web performance? Let’s find out.
www.smashingmagazine.com
HTTP/3 개요
HTTP/3의 장점으로 흔히들 알고 계시는 것들이 다음 항목이 있습니다.
- 패킷 손실이 있을 때, 손실 복구가 HTTP/2보다 훨씬 빠르다.
- HTTP/3 연결은 대기 시간이 짧고 설정하는데 시간이 적게 걸립니다.
- HTTP/3 는 데이터를 빠르게 보낼 수 있고 더 많은 리소스를 병렬로 보낼 수 있습니다.
하지만 이러한 부분들은 부분적으로만 맞는 말이며, 실제로 HTTP/3가 혁명처럼 느껴지기 보다는 진화에 가깝습니다. 너무 큰 기대를 가지게 되면 이에 대한 실망감도 크기 때문에 이번 글에서는 잘못되거나 과장된 정보를 바로잡고 HTTP/3에 대한 비현실적인 기대가 확산되는 것을 방지할 것입니다.
HTTP/3이 필요한 이유
필자는 "2015년에 표준화된 HTTP/2 이후에 얼마 지나지 않아 HTTP/3가 필요한가?" 라는 질문을 자주 접합니다. 애초에 새로운 HTTP 버전은 필요하지 않고 TCP의 업그레이드만 필요하다는 사실을 알게되기 전까지 이러한 질문은 이상합니다.
TCP는 HTTP와 같은 다른 프로토콜에 대한 신뢰성 및 순차적 전달과 같은 중요한 서비스를 제공해주는 주요 프로토콜입니다.
알고 계셨나요?
HTTP(S)를 사용하면 HTTP 외에 여러 프로토콜을 동시에 사용하게 됩니다. 각 프로토콜은 고유한 기능과 책임이 있습니다. 예를 들어, HTTP가 URL과 데이터 해석을 처리하는 반면, TLS는 암호화를 통해 보안은 보장하고 TCP는 손실된 패킷을 재전송하여 안정적인 데이터 전송을 가능하게 합니다. IP는 여러 장치를 통해 한 끝점에서 다른 끝점으로 패킷을 라우팅합니다.
TCP가 수십 년 동안 웹의 초석 역할을 했지만 2000년대 후반부터 QUIC라는 새로운 전송 프로토콜이 등장하고 QUIC는 주요 측면에서 TCP와 매우 다르기 때문에 QUIC를 기반으로 HTTP/2를 실행하는 것은 어렵습니다. 때문에 새로운 프로토콜인 QUIC와 호환되도록 HTTP/3 자체에 새로운 기능을 추가하고 HTTP/2를 작게 적용했습니다.
QUIC 프토토콜이 필요한 이유는 TCP가 최대 효율성을 염두에 두고 구축되지 않았기 때문입니다. 예를 들어 TCP는 새 연결을 설정하기 위해 "핸드 셰이크"가 필요합니다. 그러나 이는 다시말해 연결에서 다른 작업을 수행하기 전에 전체 네트워크 왕복이 필요하다는 뜻입니다. 만약 클라이언트와 서버가 지리적으로 멀리 떨어진 경우 RTT(왕복시간)이 100밀리초 이상 걸릴 수 있어 눈에 띄는 지연이 발생할 수 있습니다.
또 하나의 예로는, TCP는 실제로 동시에 여러 파일을 전송하는데 사용하더라도 전송하는 모든 데이터를 "파일" 또는 바이트 스트림으로 간주합니다. 이는 TCP 패킷이 손실되면 해당 패킷이 복구될 때 까지 다른 모든 파일도 지연된다는 것을 의미합니다. 이를 HoL(Head of Line) 차단이라고 합니다.
TCP의 이러한 문제점을 개선한 버전들이 많이 나왔지만 이를 실제로 대규모 인터넷 서비스에 사용할 수 있을만큼 확장하리란 쉬운 일이 아니였습니다. 때문에 TCP를 업그레이드 하는 대신 TCP용 대체 프로토콜이 필요하다는 것이 분명해졌습니다.
여기서 중요한 점은 우리에게 필요한 것은 "TCP/2" 였고 그 과정에서 HTTP/3을 무료로 얻었다는 것입니다. HTTP/3에 기대하는 주요 기능(빠른 연결 설정, 적은 HoL 차단, 연결 마이그레이션 등)은 실제로 모두 QUIC에서 제공됩니다.
QUIC란 무엇입니까?
QUIC는 TCP와 마찬가지로 사용될 일반 전송 프로토콜 입니다. 예를 들어 DNS, SSH, SMB, RTP 등은 모두 QUIC를 통해 실행될 수 있습니다.
QUIC는 UDP(User Datagram Protocol)이라는 프로토콜 위에서 실행됩니다. 하지만 많이들 오해하고 있는 성능 이유 때문은 아닙니다.
알고 계셨나요?
UDP는 가장 기본적인 전송 프로토콜입니다. 핸드 셰이크를 사용하여 연결을 설정하지 않고 UDP 패킷이 손실되면 자동으로 재전송되지도 않습니다. UDP는 성능이 뛰어나다는 장점을 이용하여 자주 사용됩니다. 즉, 핸드 셰이크도 없고 HoL 차단도 없으니 빠른 속도로 업데이트 되는 라이브 트래픽에 주로 사용됩니다.
많은 자료에서 성능 때문에 HTTP/3가 UDP 위에 구축되었다고 주장하지만, 이는 틀렸습니다. QUIC는 TCP를 강력하고 인기있지만 다소 느린 프로토콜로 만드는 거의 모든 기능을 본질적으로 재구현 합니다. QUIC는 수신된 패킷과 재전송에 대한 승인을 사용하여 손실된 패킷이 여전히 도착하는지 확인하므로 절대적으로 안정적입니다. 또한, QUIC는 연결을 설정하고 매우 복잡한 핸드 셰이크를 가집니다.
마지막으로 QUIC는 발신자가 네트워크나 수신자에 과부하를 주지 않도록 방지하는 흐름 제어 및 혼잡 제어 메커니즘을 사용하지만, 원시 UDP로 수행할 수 있는 것보다 TCP를 느리게 만듭니다. 중요한 것은 QUIC는 TCP보다 더 스마트하고 성능이 뛰어난 방식으로 이러한 기능을 구현한다는 것입니다.
그렇다면, QUIC는 TCP에 비해 어떻게 향상되었는지 알아봅시다. 4가지 주요 변경사항이 있습니다.
- QUIC는 TLS와 긴밀하게 통합됩니다.
- QUIC는 여러 개의 독립적인 바이트 스트림을 지원합니다.
- QUIC는 연결 ID를 사용합니다.
- QUIC는 프레임을 사용합니다.
각 특징을 하나하나 짚어가며 알아보겠습니다.
TLS 없이는 QUIC가 없습니다.
일반적인 경우 HTTP는 TCP를 통해 전송되기 전에 TLS로 암호화 됩니다.

인터넷 초기에는 트래픽을 암호화하는데 처리비용이 많이 들었습니다. 하지만 시간이 지남에 따라 기본적으로 안전함으로 인식이 변화되었습니다. 위 이미지를 보면 TLS 1.3은 여전히 TCP 위에서 독립적으로 실행될 수 있지만 QUIC는 대신 TLS 1.3을 캡슐화합니다. 다르게 말하면, TLS 없이는 QUIC를 사용할 방법이 없습니다. QUIC는 항상 완전히 암호화됩니다.

QUIC는 TCP에서와 마찬가지로 TLS 1.3 핸드 셰이크를 사용합니다. 이후에는 QUIC가 패킷 자체를 인계받아 암호화하는 반면, TLS-over-TCP를 사용하면 TLS가 자체 암호화를 수행합니다. 이러한 접근방식은 다음과 같은 몇가지 이점을 제공합니다.
1. QUIC는 사용자에게 더 안전합니다.
일반 텍스트는 QUIC를 실행할 방법이 없어 공격자와 도청자가 들을 수 있는 옵션도 적습니다.
2. QUIC의 연결 설정이 더 빠릅니다.
TLS-over-TCP의 경우 두 프로토콜 모두 별도의 핸드 셰이크가 필요하지만 QUIC는 전송 및 암호화 핸드셰이크를 하나로 결합하여 왕복 시간을 절약합니다.
3. QUIC는 더욱 쉽게 진화할 수 있습니다.
완전히 암호화되었기 때문에 네트워크의 미들박스는 더 이상 TCP에서 처럼 내부 작동을 관찰하고 해석할 수 없습니다.
하지만 몇가지 단점도 존재합니다.
1. 많은 네트워크에서 QUIC 허용을 주저할 것 입니다.
원치 않는 트래픽을 감지하는 것이 더 어려워지기 때문에 기업에서는 방화벽에서 이를 차단하고 싶을 수 있습니다. 문제를 감지하고 진단하기가 어려워져 ISP 및 중간 네트워크에서 이를 차단할 수 있습니다.
2. QUIC는 암호화 오버헤드가 더 높습니다.
QUIC는 TLS로 각 개별 패킷을 암호화하는 반면, TLS-over-TCP은 여러 패킷을 동시에 암호화할 수 있습니다. 이는 잠재적으로 높은 처리량 시나리오에서 QUIC를 더 느리게 만듭니다.
3. QUIC는 웹을 더욱 중앙 집중화합니다.
일각에선 "QUIC는 다른 사람과 데이터를 공유하지 않으면서 데이터에 대한 전체 접근 권한을 제공하기 때문에 Google이 QUIC를 추진하고 있다"는 불만사항이 자주 표출됩니다. 저는 이에 동의하지 않습니다. 왜냐하면 QUIC는 TLS-over-TCP보다 더 많은 혹은 더 적은 사용자 수준 정보를 외부 관찰자로부터 숨기지 않습니다.
여기서 중요한 점은 QUIC는 기본적으로 철저하게 암호화 된다는 것입니다. 이는 보안 및 개인 정보 보호 특성을 향상시킬 뿐만 아니라 배포 가능성과 발전 가능성에 도움이 됩니다. 프로토콜을 실행하기가 조금 더 무서워지긴 하지만 그 대가로 더 빠른 연결 설정과 같은 다른 최적화가 가능합니다.
QUIC는 다중 바이트 스트림에 대해 알고 있습니다.
HTTP/1.1에서 HTTP/2로 넘어와 변경된 것들 중 하나는 각 파일 자체의 TCP 연결을 수립하던 것에서 단일 TCP 연결을 통해 다양한 파일 다운로드를 수행할 수 있다는 것입니다. 이는 서로 다른 바이트 스트림을 "다중화"하여 달성됩니다. 이는 전송 시 서로 다른 파일의 데이터를 혼합한다는 말입니다.

하지만 TCP는 오래된 프로토콜이므로, A와 B, C에 대해 알지 못합니다. 내부적으로 TCP는 단일 파일 X만 전송한다고 생각하지만 실제로는 그렇지 않습니다. XXXXXXXXXXXX로 표시되는 내용은 실제로 HTTP 수준에서 AABCCAABBCC입니다.
만약 세번째 TCP 패킷(파일 B의 첫번째 데이터가 포함된 패킷)이 손실되고 다른 모든 데이터가 전달되었다고 가정해봅시다. TCP는 손실된 데이터의 새 복사본을 새 패킷으로 재전송하여 이러한 손실을 처리합니다. 하지만 이 과정에서 시간이 걸릴 수 있습니다. A와 C 자원에는 문제가 없으니 B에 대한 누락을 기다리면서 A와 C를 처리하면 좋을 것 같습니다.
하지만 그렇지 않습니다. 왜냐하면, 재전송 논리는 TCP계층에서 발생하고 TCP는 A, B, C에 대해 알지 못하기 때문입니다. TCP는 단일 X 파일의 일부가 손실되었다고 생각하므로 X 데이터의 구멍이 채워질 때 까지 나머지 X 데이터가 처리되지 않도록 해야한다고 생각합니다. 다르게 말하면 HTTP/2 수준에서 우리는 이미 A, C를 처리할 수 있다는 것을 알지만 TCP는 이를 모르기에 속도가 느려집니다. 이러한 비효율성은 "HoL(Head-of-Line) 차단" 문제의 한 예시입니다.
전송계층에서 HoL 차단 문제를 해결하는 것이 QUIC의 주요 목표 중 하나였습니다. TCP와 달리 QUIC는 여러 개의 독립적인 바이트 스트림을 다중화하고 있습니다. 물론 CSS 파일인지 JavaScript 파일인지는 모릅니다. 단지 스트림이 분리되어 있다는 것만 알고 있습니다. 따라서 QUIC는 스트림별로 패킷 손실 감지 및 복구 로직을 수행할 수 있습니다.
QUIC는 연결 마이그레이션을 지원합니다.
시스템과 애플리케이션 전반에 걸쳐 고유한 연결을 정의하려면 클라이언트 IP 주소 + 클라이언트 포트 + 서버 IP 주소 + 서버 포트 라는 4가지 튜플이 필요합니다. TCP에서 연결은 4-튜플로만 식별됩니다. 만약 4가지 변수 중 하나만 변경되면 연결이 무효화되고 다시 설정되어야합니다.
TCP 연결을 재시작하면 심각한 영향을 끼칠 수 있기 때문에 이를 해결하기 위해 QUIC는 연결 식별자(CID)라는 새로운 개념을 도입합니다. 각 연결에는 두 끝점 사이에서 연결을 고유하게 식별하는 4-튜플 위에 다른 번호가 할당됩니다. 결정적으로 이 CID는 QUIC 자체의 전송 계층에서 정의되므로 네트워크 간 이동 시 변경되지 않습니다.

이러면 4-튜플 중 하나가 변경되더라도 QUIC 서버와 클라이언트는 CID만 보고 그것이 동일한 이전 연결인지 식별하여 계속 사용할 수 있습니다. 새로운 핸드 셰이크가 필요없으며 다운로드 상태가 그대로 유지될 수 있습니다. 그래서 우리가 카페 안에서 Wi-Fi로 다운받던 파일을 카페 외부로 나가 셀룰러 데이터로 변경되어도 연결이 끊기지 않고 이어서 다운로드 받을 수 있는 것입니다.
CID가 극복해야한 과제 중 하나는 보안입니다. 단일 CID만 사용한다면 해커와 도청자가 네트워크를 통해 사용자를 추적하여 물리적인 위치를 추론하는 것이 매우 쉬워집니다. 때문에 QUIC는 새 네트워크가 사용될 때 마다 CID를 변경합니다.

예를 들어 클라이언트와 서버는 CID K, C, C가 모두 연결 X에 매핑된다는 것을 알고 있습니다. 따라서 클라이언트는 Wi-Fi에서 패킷에 K로 태그를 지정할 수 있지만 4G에서는 C를 사용하도록 전환할 수 있습니다. 이러한 공통 목록은 QUIC에서 완전히 암호화되어 협상되므로 해커가 K와 C가 실제로 X라는 것을 알지 못하게 합니다. 클라이언트와 서버가 서로 다른 포트번호를 갖는 것처럼 서로 다른 CID 목록을 갖기 때문에 이는 더 복잡해집니다.
여기서 중요한 점은 TCP에서 연결은 엔드포인트가 네트워크를 변경할 때, 변경될 수 있는 4가지 매개변수로 정의된다는 것입니다.
QUIC는 유연하고 진화 가능합니다.
QUIC는 쉽게 발전할 수 있도록 제작되었습니다. 이는 여러가지 방법으로 수행됩니다. 첫째, QUIC가 완전히 암호화 된다는 사실은 QUIC를 배포하려는 경우 미들박스가 아닌 엔드포인트(클라이언트 및 서버)만 업데이트 하면 된다는 것을 의미합니다.
둘째, TCP와 달리 QUIC는 모든 프로토콜 메타 데이터를 전송하기 위해 단일 고정 패킷 헤더를 사용하지 않습니다. 대신 QUIC는 짧은 해킷 헤더를 가지며 패킷 페이로드 내부의 다양한 프레임을 사용하여 추가 정보를 전달합니다. 이는 모든 패킷이 모든 메타 데이터를 전달하는 것이 아니기에 최적화를 위해 수행됩니다. TCP 패킷 헤더는 일반적으로 상당한 바이트를 낭비합니다.

셋째, QUIC는 사용자 정의 TLS 확장을 사용하여 전송 매개변수를 전달합니다. 이를 통해 클라이언트와 서버는 QUIC 연결에 대한 구성을 선택할 수 있습니다. 이는 어떤 기능이 활성화되는지(예: 연결 마이그레이션 허용 여부, 지원되는 확장 등) 협상하고 일부 메커니즘에 대한 합리적인 기본값(예: 최대 지원 패킷 크기, 흐름 제어 제한)을 전달할 수 있음을 의미합니다.
마지막으로 QUIC 자체의 실제 요구사항은 아니지만 대부분의 구현은 현재 "사용자 공간"에서 수행됩니다. 일반적으로 TCP는 "커널 공간"에서 수행되는 것과는 반대입니다.
결론
TCP를 발전시키기 위해 노력하면서 거의 모든 장치에 TCP가 탑재되어 있어 이것이 어려울 것이라고 판단했습니다. TCP를 개선하면서 이 문제를 해결하기 위해 새로운 QUIC 프로토콜(실제로는 TCP 2.0)을 만들었습니다. QUIC를 더 쉽게 배포할 수 있도록 UDP 프로토콜 위에서 실행되며, 향후 발전할 수 있도록 완전히 암호화되고 유연한 프레이밍을 사용합니다.
이외에도 QUIC는 핸드셰이크, 안정성 및 혼잡 제어와 같은 알려진 TCP 기능 대부분을 미러링합니다. 이외에 두가지 변경 사항은 다중 바이트 스트림에 대한 인식과 연결 ID의 도입입니다. 이로인해 QUIC 위에서 HTTP/2를 직접 실행하는 것이 불가능해졌고 HTTP/3 생성이 필요해졌습니다.