HTTP/1.1 vs HTTP/2 vs HTTP/3
웹의 근간인 HTTP 프로토콜의 진화. 각 버전이 어떤 문제를 해결하기 위해 등장했는지, 핵심 차이가 무엇인지 비교한다.
HTTP 버전 한눈에 보기
HTTP/1.1 — 오래된 거인
HTTP/1.1은 1997년 등장해 10년 넘게 웹의 유일한 표준으로 사용됐다. 현재도 전 세계 수많은 서버에서 동작하고 있다. 텍스트 기반의 단순한 설계가 이해하기 쉽고 디버깅이 편하다는 장점이 있지만, 성능 한계가 명확하다.
Keep-Alive 연결
HTTP/1.0은 요청마다 TCP 연결을 새로 맺었다. HTTP/1.1은 Keep-Alive로 하나의 TCP 연결을 재사용한다. 하지만 한 연결에서 요청은 여전히 순차적이다 — 앞 요청의 응답을 받아야 다음 요청을 보낼 수 있다.
파이프라이닝의 한계
HTTP/1.1은 파이프라이닝(Pipelining)으로 응답을 기다리지 않고 요청을 연속 전송할 수 있도록 했다. 하지만 서버는 요청 순서대로 응답해야 해서 첫 번째 응답이 느리면 이후 응답도 모두 기다려야 한다 — 이를 HOL(Head-of-Line) Blocking이라 한다. 브라우저들이 파이프라이닝을 거의 사용하지 않는 이유다.
HTTP/2 — 바이너리 멀티플렉싱
HTTP/2는 2015년 Google의 SPDY 프로토콜을 기반으로 표준화됐다. HTTP/1.1과 의미론(Semantics)은 동일하다 — 메서드(GET, POST...), 헤더, 상태 코드가 그대로다. 바뀐 것은 데이터를 전송하는 방식이다.
핵심 기능 1: 바이너리 프레이밍
HTTP/1.1이 텍스트 기반이라면, HTTP/2는 모든 메시지를 바이너리 프레임으로 분해한다. 프레임은 더 효율적으로 파싱되고, 멀티플렉싱의 기반이 된다.
핵심 기능 2: 스트림 멀티플렉싱
HTTP/2는 하나의 TCP 연결 위에 여러 스트림(Stream)을 동시에 열 수 있다. 각 스트림은 독립적인 요청-응답 쌍이며, 순서와 관계없이 인터리빙되어 전송된다. HOL Blocking이 해결된다.
index
css
js
index
css
js
핵심 기능 3: HPACK 헤더 압축
HTTP/1.1에서는 매 요청마다 동일한 헤더(Host, Cookie, Accept, User-Agent...)가 반복 전송된다. HTTP/2의 HPACK은 헤더를 압축하고 이전에 전송한 헤더를 정적/동적 테이블에 저장해 인덱스 번호만 보낸다. 반복 헤더 전송량을 크게 줄인다.
핵심 기능 4: 서버 푸시
서버가 클라이언트의 요청 없이 리소스를 미리 보내는 기능이다. HTML 응답과 함께 CSS, JS를 미리 푸시하면 클라이언트가 HTML을 파싱한 후 다시 요청하는 지연을 없앤다. 다만 실제 활용이 예측하기 어렵고 캐시 중복 문제가 있어 HTTP/2에서도 실무 사용이 줄었고, HTTP/3에서는 제거되었다.
HTTP/3 — QUIC으로의 전환
HTTP/3는 2022년 RFC 9114로 표준화됐다. 가장 큰 변화는 전송 계층을 TCP에서 QUIC(UDP 기반)으로 교체한 것이다.
TCP HOL Blocking 완전 해결
QUIC은 각 스트림을 완전히 독립적으로 관리한다. 스트림 3의 패킷이 유실되어도 스트림 1, 5는 영향받지 않는다. TCP에서는 불가능했던 진정한 스트림 독립성이 실현된다.
0-RTT 연결 수립
이전에 접속했던 서버에 재연결 시, QUIC은 저장된 세션 정보로 0-RTT — 즉, 핸드셰이크 없이 첫 번째 패킷에 데이터를 실어 보낼 수 있다. 최초 연결도 TLS를 내장해 1-RTT로 완료된다.
연결 마이그레이션
TCP 연결은 4-튜플(src IP, src Port, dst IP, dst Port)로 식별된다. 스마트폰이 Wi-Fi에서 LTE로 전환되면 IP가 바뀌어 TCP 연결이 끊긴다. QUIC은 Connection ID로 연결을 식별하므로 IP가 바뀌어도 연결이 유지된다.
버전별 비교표
| 특성 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 전송 계층 | TCP | TCP | QUIC (UDP) |
| 메시지 형식 | 텍스트 | 바이너리 프레임 | 바이너리 프레임 |
| 멀티플렉싱 | ❌ (연결 여러 개로 우회) | ✅ 스트림 | ✅ 스트림 |
| 앱 레벨 HOL | ❌ 존재 | ✅ 해결 | ✅ 해결 |
| TCP 레벨 HOL | ❌ 존재 | ❌ 존재 | ✅ 해결 (QUIC) |
| 헤더 압축 | ❌ 없음 | ✅ HPACK | ✅ QPACK |
| 서버 푸시 | ❌ | ✅ (실무 활용 제한) | ❌ (제거됨) |
| 연결 수립 | 1 RTT + TLS | 1 RTT + TLS | 1 RTT (0-RTT 재연결) |
| 연결 마이그레이션 | ❌ | ❌ | ✅ Connection ID |
| 암호화 | HTTPS 선택 | 사실상 필수 | 필수 (내장) |
실습: 버전 확인하기
# curl로 HTTP 버전 확인 $ curl -sI --http1.1 https://www.google.com | head -1 HTTP/1.1 200 OK $ curl -sI --http2 https://www.google.com | head -1 HTTP/2 200 $ curl -sI --http3 https://www.google.com | head -1 # curl 7.88+ 필요 HTTP/3 200 # 브라우저 개발자 도구 → Network 탭 → Protocol 열에서 h1/h2/h3 확인 가능
# Nginx HTTP/2 설정
server {
listen 443 ssl http2;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
...
}
# Nginx HTTP/3 (QUIC) 설정 (nginx 1.25+)
server {
listen 443 quic reuseport; # UDP
listen 443 ssl http2; # TCP fallback
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
add_header Alt-Svc 'h3=":443"; ma=86400';
}
댓글