HTTP/2(RFC 9113)는 HTTP/1.1의 의미(메서드·상태 코드·헤더)는 그대로 두면서 전송 방식만 완전히 재설계한 프로토콜입니다. 목표는 하나의 TCP 연결로 여러 요청을 동시에, 지연 없이 처리하는 것이었습니다. HTTP/1.1이 연결당 한 번에 하나의 요청만 처리해 생기던 병목(그리고 브라우저가 도메인당 6개씩 연결을 열던 낭비)을 없애기 위함입니다.
바이너리 프레이밍
가장 근본적인 변화는 텍스트 기반이던 HTTP를 바이너리 프레임(frame)으로 바꾼 것입니다. 모든 통신은 고정된 9바이트 헤더를 가진 프레임 단위로 쪼개집니다. 프레임에는 타입(HEADERS, DATA, SETTINGS, WINDOW_UPDATE, RST_STREAM 등)과, 자신이 속한 스트림 식별자(Stream ID)가 붙습니다.
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload ... |
+---------------------------------------------------------------+
스트림과 멀티플렉싱
스트림(stream)은 하나의 요청·응답 교환에 대응하는 논리적 채널이며, 각자 고유한 Stream ID를 가집니다. 여러 스트림의 프레임을 하나의 TCP 연결에 뒤섞어(interleave) 보내는 것이 멀티플렉싱(multiplexing)입니다. 수신 측은 각 프레임의 Stream ID를 보고 올바른 요청으로 재조립합니다. 덕분에 느린 응답 하나가 뒤의 요청들을 막던 HTTP/1.1의 애플리케이션 계층 head-of-line blocking이 사라지고, 연결 하나로 수백 개의 요청을 병렬 처리할 수 있습니다.
HPACK 헤더 압축
HTTP 요청 헤더는 User-Agent, Cookie처럼 요청마다 거의 똑같이 반복되어 낭비가 큽니다. HPACK(RFC 7541)은 이를 압축합니다. 자주 쓰는 헤더를 담은 정적 테이블과, 연결마다 양쪽이 함께 갱신하는 동적 테이블을 두고, 헤더를 실제 문자열 대신 테이블 인덱스로 참조합니다. 문자열 값은 허프만 부호화로 추가 압축합니다. (HTTP/1.1에는 헤더 압축이 아예 없었습니다.)
- 바이너리 프레이밍: 텍스트 대신 프레임 단위로 파싱이 빠르고 명확.
- 멀티플렉싱: 한 연결로 여러 스트림 동시 처리 → 연결 수 급감.
- HPACK: 반복되는 헤더를 인덱스로 압축.
- 스트림 우선순위: 어떤 스트림을 먼저 보낼지 힌트.
- 플로 컨트롤: 스트림·연결 단위로 수신 속도 조절.
- server push: 서버가 요청 전에 자원을 미리 밀어넣기(현재 폐기).
Server push(폐기), 우선순위, 플로 컨트롤
server push는 클라이언트가 HTML을 요청하면 서버가 필요한 CSS·JS를 요청 전에 미리 밀어 보내는 기능이었으나, 캐시된 자원까지 중복 전송하는 등 실효가 낮아 주요 브라우저가 지원을 제거해 사실상 폐기되었습니다(오늘날에는 103 Early Hints나 preload가 그 자리를 대신합니다). 스트림 우선순위(prioritization)는 어떤 스트림을 먼저 보낼지 힌트를 주는 메커니즘인데, 초기 트리 기반 방식은 복잡해 잘 안 쓰였고 이후 단순한 우선순위 체계로 대체되는 흐름입니다. 플로 컨트롤(flow control)은 WINDOW_UPDATE 프레임으로 스트림·연결 단위 수신 윈도를 관리해, 빠른 송신자가 느린 수신자를 압도하지 않게 합니다.
h2c와 남은 한계: TCP head-of-line blocking
HTTP/2는 사실상 TLS(ALPN h2) 위에서 협상됩니다. 암호화 없는 평문 HTTP/2를 h2c라고 부르지만, 브라우저는 h2c를 지원하지 않아 주로 내부 서비스 간 통신에서만 쓰입니다. 그리고 HTTP/2가 애플리케이션 계층 HoL은 없앴지만 전송 계층의 한계는 남습니다. 모든 스트림이 하나의 TCP 연결을 공유하므로, 그 연결에서 패킷 하나가 손실되면 TCP가 재전송·순서 보장을 위해 뒤따르는 모든 스트림의 데이터까지 함께 멈춰 세웁니다. 이것이 TCP head-of-line blocking이며, 이 문제를 근본적으로 해결하려고 등장한 것이 UDP 기반의 QUIC(HTTP/3)입니다.