iBetter Books
수정

HTTP 1.1에서 2, 3 그리고 QUIC

웹이 커지면서 HTTP도 진화해야 했습니다. 1997년에 표준화된 HTTP/1.1은 25년 넘게 인터넷의 중심을 지탱했지만, 웹 페이지가 수십 개의 리소스를 동시에 불러오는 시대에는 성능 한계가 드러났습니다. HTTP/2와 HTTP/3는 그 한계를 극복하기 위해 등장했습니다.

HTTP/1.1의 한계 — HOL 블로킹

HTTP/1.1에서 하나의 TCP 연결은 한 번에 하나의 요청-응답만 처리합니다. 페이지 하나를 로딩할 때 이미지 20개, CSS 5개, JS 10개가 필요하다면, 이것들을 순서대로 기다려야 합니다. 브라우저는 이를 극복하기 위해 도메인당 6~8개의 TCP 연결을 병렬로 열었습니다. TCP 연결마다 3-way 핸드셰이크가 필요하므로 그 자체가 오버헤드입니다.

더 근본적인 문제는 HOL(Head-of-Line) 블로킹입니다. 앞선 요청의 응답이 아직 오지 않았으면, 뒤따르는 요청은 전송 순서를 기다려야 합니다. 줄 맨 앞에 서 있는 요청이 느리면 뒤의 요청들이 전부 막힙니다.

HTTP/2 — 멀티플렉싱

HTTP/2는 2015년에 등장했습니다. 가장 핵심적인 변화는 멀티플렉싱(multiplexing)입니다.

HTTP/2는 하나의 TCP 연결 위에 여러 개의 스트림(stream)을 논리적으로 열어 둡니다. 각 요청-응답은 독립적인 스트림에 실립니다. 스트림들은 서로 순서를 기다리지 않고 동시에 진행됩니다. TCP 연결을 여러 개 열 필요가 없습니다. 하나의 연결로 충분합니다.

헤더도 압축합니다. HTTP/1.1에서는 헤더가 매 요청마다 텍스트 그대로 전송됩니다. HTTP/2는 HPACK이라는 방식으로 헤더를 압축하고, 이전 요청에서 이미 보낸 헤더는 다시 보내지 않습니다.

HTTP/2의 한계와 TCP HOL 블로킹

HTTP/2가 애플리케이션 레벨의 HOL 블로킹을 해결했지만, 새로운 문제가 드러났습니다. 전송계층인 TCP 자체의 HOL 블로킹입니다.

PART 05에서 배웠듯이, TCP는 순서를 보장합니다. 네트워크에서 패킷이 하나 유실되면, TCP는 그 패킷을 재전송받을 때까지 이후 패킷들을 애플리케이션에 전달하지 않습니다. HTTP/2가 하나의 TCP 연결 위에 20개의 스트림을 올려놓았다면, 패킷 하나가 유실되는 순간 20개의 스트림이 모두 멈춥니다. 이것이 TCP HOL 블로킹입니다.

HTTP/3와 QUIC — UDP 위에서 신뢰성 구현

HTTP/3는 2022년에 RFC 9114로 표준화되었습니다. 가장 파격적인 결정은 TCP를 버리는 것이었습니다.

HTTP/3는 QUIC(Quick UDP Internet Connections) 프로토콜을 사용합니다. QUIC는 UDP 위에서 동작합니다. PART 05에서 UDP는 신뢰성 없는 프로토콜이라고 배웠는데, 어떻게 된 일인가 싶을 것입니다.

QUIC는 UDP의 빠른 속도는 취하되, 신뢰성·순서 보장·흐름 제어를 스스로 구현합니다. TCP가 운영체제 커널에 박혀 있어 바꾸기 어려운 것과 달리, QUIC는 주로 애플리케이션 쪽 사용자 공간(user space)에서 구현되는 경우가 많아 빠르게 발전할 수 있습니다.

QUIC가 해결하는 핵심 문제는 스트림별 독립성입니다. QUIC에서 패킷 하나가 유실되어도 그 스트림만 잠깐 멈춥니다. 다른 스트림들은 계속 진행됩니다. TCP의 HOL 블로킹이 근본적으로 사라집니다.

연결 수립도 빠릅니다. TCP는 3-way 핸드셰이크 후에 TLS 협상을 별도로 합니다. QUIC는 첫 번째 왕복(RTT)에 연결 수립과 TLS 협상을 함께 처리합니다. 재연결 시에는 0-RTT, 즉 이전 연결 정보를 사용해 핸드셰이크 없이 바로 데이터를 보낼 수도 있습니다.

오늘날 Google, YouTube, 페이스북, Cloudflare 등 주요 서비스가 HTTP/3와 QUIC를 지원합니다. 브라우저 주소창에 자물쇠 아이콘이 보이는 사이트들 중 많은 수가 이미 HTTP/3로 통신하고 있습니다.

다음 절에서는 HTTP를 안전하게 만드는 HTTPS와 TLS 핸드셰이크를 살펴봅니다.