-
HTTP HOL Blocking에서 QUIC Stream까지CS/네트워크 2026. 6. 29. 21:07
HTTP/2는 하나의 TCP 연결에 여러 HTTP Stream을 Multiplexing해 HTTP 계층의 순차 대기를 줄였고, HTTP/3는 독립된 QUIC Stream을 사용해 한 Stream의 패킷 유실이 다른 Stream의 전달까지 막는 TCP 계층 HOL Blocking을 줄였다.
Stream은 하나의 연결 안에 만드는 논리적인 바이트 통로다. HTTP/2는 여러 통로처럼 보이지만 밑에서는 하나의 TCP 통로를 함께 사용한다. 그래서 TCP 데이터가 유실되면 모든 Stream이 기다릴 수 있다. HTTP/3는 QUIC이 Stream별 통로를 직접 관리하기 때문에 한 Stream의 유실이 다른 Stream의 순서까지 막지 않는다.
TCP는 연결 방향마다 하나의 바이트 순서 공간을 사용한다. 중간 바이트가 유실되면 뒤에 다른 HTTP Stream의 데이터가 도착해도 소켓은 빠진 부분 이후의 바이트를 애플리케이션에 먼저 전달할 수 없다.
QUIC은 Packet Number로 전송과 유실을 추적하고, Stream ID와 Stream Offset으로 데이터가 어느 Stream의 몇 번째 바이트인지 구분한다. 한 Stream에 빈 구간이 생겨도 다른 Stream의 연속된 데이터는 애플리케이션에 전달할 수 있다.
HTTP/1.1은 왜 여러 TCP 연결을 사용했을까
HTTP/1.1은 Keep-Alive를 이용해 하나의 TCP 연결을 여러 요청에서 재사용할 수 있다. 하지만 일반적인 순차 요청에서는 앞 요청의 응답을 기다리는 동안 다음 요청을 처리하지 못한다.
Pipelining을 사용하면 응답을 기다리지 않고 여러 요청을 먼저 보낼 수 있다. 그러나 HTTP/1.1 서버는 Pipelining 요청의 응답을 요청 순서대로 보내야 한다. 첫 번째 요청의 처리가 오래 걸리면 뒤에서 이미 끝난 응답도 먼저 보낼 수 없다. 이것이 HTTP 계층의 HOL, Head-of-Line Blocking이다.
브라우저는 이 문제를 완화하려고 같은 서버에 여러 TCP 연결을 병렬로 열었다. 연결 개수는 표준이 고정하지 않으며 구현에 따라 달라진다. 여러 연결은 병렬성을 얻지만 TCP·TLS 연결 비용과 서버 자원 사용량도 늘린다.
HTTP/2 Multiplexing
HTTP/2는 HTTP 메시지를 작은 Frame으로 나누고 Stream ID를 붙인다. 여러 Stream의 Frame을 하나의 TCP 연결에 교차 배치할 수 있다.
하나의 HTTP/2 TCP 연결 HEADERS(Stream 1) HEADERS(Stream 3) DATA(Stream 3) DATA(Stream 1)Stream 1의 애플리케이션 처리가 느리더라도 준비된 Stream 3의 Frame을 먼저 보낼 수 있다. HTTP/1.1의 응답 순서 제약에서 발생한 애플리케이션 계층 HOL Blocking을 줄이는 방식이다.
HTTP/2에도 TCP HOL Blocking이 발생한다
HTTP/2의 여러 Stream은 결국 하나의 TCP 바이트 스트림에 실린다. TCP는 애플리케이션에 순서대로 이어진 바이트만 전달한다.
TCP 바이트 구간 A: Stream 1 데이터 — 유실 TCP 바이트 구간 B: Stream 3 데이터 — 도착수신 TCP가 B를 버리지 않고 버퍼에 저장하더라도, A가 재전송되기 전에는 B를 HTTP/2 계층에 전달할 수 없다. Stream 1의 손실이 Stream 3까지 멈추게 한다.
QUIC Stream이란 무엇인가
QUIC 연결 안에는 여러 개의 독립적인 Stream이 존재한다. Stream은 애플리케이션에 순서 있는 바이트 통로를 제공한다.
QUIC Connection ├── Stream 0: 요청 A의 순서 있는 바이트 ├── Stream 4: 요청 B의 순서 있는 바이트 └── Stream 8: 요청 C의 순서 있는 바이트HTTP/3에서는 일반적으로 하나의 요청·응답 쌍이 하나의 양방향 QUIC Stream을 사용한다. 같은 Stream 안에서는 데이터 순서를 보장하지만, 서로 다른 Stream 사이에는 하나의 전체 전달 순서를 강제하지 않는다.
예를 들어 Stream 0의 중간 데이터가 유실돼도 Stream 4의 데이터가 연속적으로 도착했다면 Stream 4는 계속 처리할 수 있다. 이것이 HTTP/3가 TCP 계층 HOL Blocking을 줄이는 핵심이다.
Packet Number와 Stream Offset
QUIC은 전송 순서와 데이터 전달 순서를 서로 다른 값으로 관리한다.
값 관리 대상 역할
Packet Number QUIC 패킷 어떤 전송이 ACK됐거나 유실됐는지 추적 Stream ID 논리적 Stream 데이터가 어느 Stream에 속하는지 구분 Stream Offset Stream 내부 바이트 해당 데이터가 Stream의 몇 번째 위치인지 표시 TCP Sequence Number는 바이트 스트림의 위치를 나타내며 같은 바이트를 재전송할 때 같은 Sequence 범위를 사용한다. 반면 QUIC Packet Number는 전송 자체를 식별한다. 유실된 Stream 데이터를 다시 보낼 때는 같은 Stream Offset의 데이터를 새로운 Packet Number를 가진 패킷에 담는다.
Packet 100: Stream 0, Offset 0~999 — 유실 Packet 101: Stream 4, Offset 0~999 — 도착 Packet 102: Stream 0, Offset 0~999 — 새 Packet Number로 재전송Packet Number는 ACK와 유실 감지에 사용하고, Stream Offset은 재전송된 데이터까지 포함해 애플리케이션 바이트 순서를 복원하는 데 사용한다.
QUIC은 UDP가 하지 않는 일을 직접 제공한다
UDP는 신뢰성, 순서, 재전송, 흐름 제어와 혼잡 제어를 제공하지 않는다. QUIC은 UDP 위에서 동작하지만 다음 전송 기능을 직접 구현한다.
1. 패킷 유실 감지와 재전송
수신자는 ACK 범위로 받은 Packet Number를 알린다. 송신자는 더 나중의 패킷이 ACK됐는데 앞 패킷이 일정 패킷 수 또는 시간 이상 ACK되지 않으면 유실로 판단한다. 응답이 오지 않을 때는 PTO, Probe Timeout을 이용해 복구를 시도한다.
유실된 패킷 자체를 같은 번호로 다시 보내는 것이 아니라, 필요한 Frame 데이터를 새로운 Packet Number의 패킷에 다시 담는다.
2. Stream과 Connection 흐름 제어
QUIC은 두 단계로 수신자의 처리 능력을 보호한다.
- MAX_STREAM_DATA: 특정 Stream이 보낼 수 있는 최대 바이트 위치를 알린다.
- MAX_DATA: 연결의 모든 Stream을 합쳐 보낼 수 있는 전체 데이터 한도를 알린다.
하나의 Stream이 수신 버퍼를 독점하지 못하도록 Stream별 한도와 연결 전체 한도를 함께 사용한다.
3. 연결 전체 혼잡 제어
Stream의 전달 순서는 독립적이어도 네트워크 경로의 대역폭은 공유한다. QUIC 혼잡 제어는 일반적으로 연결의 경로 단위로 동작한다.
RFC 9002는 TCP NewReno와 비슷한 혼잡 제어를 예시로 정의한다. 혼잡 회피에서는 AIMD 방식으로 윈도를 늘리고 손실이나 ECN 혼잡 신호가 발생하면 줄인다. 다만 QUIC이 NewReno만 강제하는 것은 아니며 CUBIC 같은 다른 송신 측 알고리즘도 사용할 수 있다.
따라서 HTTP/3가 한 Stream의 순서 대기를 다른 Stream과 분리해도, 패킷 손실로 연결 전체의 혼잡 윈도가 감소하면 다른 Stream의 처리율도 영향을 받을 수 있다.
결론
HTTP/1.1의 Pipelining은 응답 순서 때문에 HOL Blocking이 발생한다. HTTP/2는 하나의 TCP 연결에서 여러 HTTP Stream을 Multiplexing해 이 애플리케이션 계층 제약을 줄였다. 하지만 TCP가 연결 전체를 하나의 순서 있는 바이트 스트림으로 제공하기 때문에 패킷 유실 시 모든 HTTP/2 Stream이 기다릴 수 있다.
HTTP/3의 QUIC은 Packet Number로 전송을 추적하고 Stream ID와 Stream Offset으로 전달 순서를 Stream별로 분리한다. 그래서 한 Stream의 유실이 다른 Stream의 데이터 전달까지 직접 막지 않는다. 다만 흐름 제어와 혼잡 제어 자원은 연결과 경로 수준에서도 공유되므로 모든 성능 문제가 사라지는 것은 아니다.
요약
- HOL Blocking이 어느 계층에서 어떻게 생기는지 구분한다.
- Multiplexing은 여러 논리적 작업을 하나의 연결에 교차 배치하는 것이다.
- TCP는 연결 전체의 전달 순서를, QUIC은 Stream별 전달 순서를 제공한다.
- QUIC Packet Number는 전송 순서, Stream Offset은 애플리케이션 데이터 순서를 나타낸다.
- Stream 독립성과 네트워크 혼잡 자원 공유는 동시에 존재한다.
'CS > 네트워크' 카테고리의 다른 글
TCP 3-Way Handshake (0) 2026.07.24 주소창에 도메인을 입력하면, IP는 어디서 올까? DNS 조회의 전체 흐름 (0) 2026.07.23 HTTP Stateless와 Keep-Alive, 세션과 JWT (0) 2026.07.13