HTTP/1.1, HTTP/2, HTTP/3의 주요 차이점을 설명해주세요.
힌트
멀티플렉싱, HOL 블로킹, QUIC 프로토콜을 생각해보세요.
정답 및 해설
HTTP/1.1, HTTP/2, HTTP/3의 주요 차이점을 설명해주세요.
HTTP는 웹의 근간이 되는 프로토콜로, 버전이 올라갈수록 성능과 효율이 크게 향상되었습니다. 각 버전은 이전 버전의 한계를 극복하기 위해 설계되었으며, 네트워크 계층 수준의 근본적인 변화를 포함합니다.
HTTP/1.1
1997년 표준화된 HTTP/1.1은 현재도 널리 사용됩니다.
특징
텍스트 기반 프로토콜
요청과 응답이 사람이 읽을 수 있는 ASCII 텍스트 형태입니다.
GET /index.html HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: text/html
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234
<html>...</html>
Keep-Alive (지속 연결)
HTTP/1.0은 요청마다 TCP 연결을 새로 맺었지만, HTTP/1.1은 Keep-Alive로 하나의 TCP 연결을 재사용합니다.
-- 연결 재사용 --
Connection: keep-alive
Keep-Alive: timeout=5, max=100
HOL 블로킹 (Head-of-Line Blocking)
HTTP/1.1의 가장 큰 문제입니다. 하나의 TCP 연결에서 이전 요청의 응답이 오기 전에 다음 요청을 보낼 수 없습니다.
TCP 연결 1:
요청1 →
← 응답1 (대용량 이미지, 3초 소요)
요청2 → ← 요청1이 끝나야 시작 가능!
← 응답2
이를 극복하기 위해 브라우저는 도메인당 6개의 병렬 TCP 연결을 사용합니다. 이를 이용해 여러 리소스를 동시에 받지만, 연결 수 제한이 있습니다.
워터폴 문제
타임라인:
────────────────────────────────────→ 시간
| HTML |
| CSS | JS |
| 이미지1 | 이미지2 | 이미지3 |
리소스들이 순차적으로 다운로드되어 전체 페이지 로드 시간이 길어집니다.
도메인 샤딩 우회 기법
<!-- HTTP/1.1 시대의 꼼수: 여러 서브도메인으로 병렬 연결 수 증가 -->
<img src="https://img1.cdn.com/a.jpg">
<img src="https://img2.cdn.com/b.jpg">
<img src="https://img3.cdn.com/c.jpg">
<!-- 각 도메인마다 6개 연결 → 18개 병렬 연결 -->
HTTP/2
2015년 Google의 SPDY 프로토콜을 기반으로 표준화된 HTTP/2는 성능을 대폭 향상시켰습니다.
바이너리 프레임 계층
HTTP/1.1의 텍스트 기반 대신 바이너리 프레임으로 데이터를 전송합니다.
HTTP/1.1: 텍스트 파싱 → 에러 가능성, 처리 복잡
HTTP/2: 바이너리 → 파싱 효율적, 에러 감소
멀티플렉싱 (Multiplexing)
단일 TCP 연결에서 여러 요청/응답을 동시에 처리합니다. 각 요청은 독립적인 스트림(stream) 으로 관리됩니다.
HTTP/1.1:
TCP 연결: [요청1]→ ←[응답1] [요청2]→ ←[응답2] (순차)
HTTP/2:
TCP 연결: [스트림1: 요청1]→ [스트림2: 요청2]→ [스트림3: 요청3]→
←[스트림2: 응답2] ←[스트림1: 응답1] ←[스트림3: 응답3]
(응답 순서 상관없이 완료 순으로 반환)
HOL 블로킹이 애플리케이션 계층에서는 해결됩니다.
헤더 압축 (HPACK)
HTTP/1.1은 매 요청마다 동일한 헤더를 반복 전송합니다. HTTP/2는 HPACK 압축으로 헤더 오버헤드를 크게 줄입니다.
HTTP/1.1 (매 요청마다 반복):
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)... -- 500바이트
Accept: text/html,application/xhtml+xml... -- 300바이트
Cookie: session=abc123; preferences=dark... -- 200바이트
HTTP/2 (HPACK으로 압축):
이전에 보낸 헤더는 인덱스로 참조 → 수십 바이트로 압축
서버 푸시 (Server Push)
클라이언트가 요청하기 전에 서버가 필요한 리소스를 미리 전송합니다.
클라이언트: GET /index.html
서버: 응답 + CSS 푸시 + JS 푸시 + 폰트 푸시
(클라이언트가 HTML을 파싱하기 전에 이미 리소스 수신 시작)
실무에서는 예측이 어렵고 불필요한 데이터를 전송할 수 있어 많이 사용되지 않습니다. HTTP/3에서는 제거되었습니다.
스트림 우선순위
// 중요한 CSS를 JS보다 우선 전송
// 브라우저가 자동으로 우선순위 협상
// 서버는 가능하면 높은 우선순위 스트림을 먼저 처리
HTTP/2의 한계: TCP 수준 HOL 블로킹
멀티플렉싱으로 애플리케이션 계층의 HOL 블로킹은 해결했지만, TCP 계층의 HOL 블로킹은 여전히 존재합니다.
TCP 패킷 손실 시:
패킷 1 ✓
패킷 2 ✗ (손실) ← 재전송 대기
패킷 3 ✓ (버퍼에서 대기)
패킷 4 ✓ (버퍼에서 대기)
→ 패킷 2가 재전송될 때까지 패킷 3,4도 사용 불가
→ 모든 HTTP/2 스트림이 블로킹됨
특히 패킷 손실이 잦은 불안정한 네트워크(모바일, Wi-Fi)에서 HTTP/1.1보다 오히려 느릴 수 있습니다.
HTTP/3
2022년 표준화된 HTTP/3는 TCP 대신 QUIC(Quick UDP Internet Connections) 프로토콜을 사용합니다.
QUIC: UDP 기반의 새로운 전송 계층
HTTP/1.1, HTTP/2: IP → TCP → TLS → HTTP
HTTP/3: IP → UDP → QUIC(TLS 1.3 내장) → HTTP/3
연결 수립 속도 향상
HTTP/1.1 + TLS 1.2:
TCP SYN → TCP SYN-ACK → TCP ACK (1.5 RTT)
TLS Hello → TLS Hello → TLS Finished... (2 RTT)
총 3.5 RTT 후 데이터 전송 시작
HTTP/2 + TLS 1.3:
TCP 3-way handshake (1.5 RTT)
TLS 1.3 handshake (1 RTT)
총 2.5 RTT
HTTP/3 + QUIC:
QUIC Initial (0-RTT 또는 1-RTT) — TLS가 QUIC에 통합
첫 연결: 1 RTT
재연결: 0-RTT (이전 연결 정보 재사용)
독립 스트림 (TCP HOL 블로킹 완전 해결)
QUIC는 각 스트림을 완전히 독립적으로 관리합니다.
QUIC 스트림:
스트림 1: 패킷 A ✓, 패킷 B ✓
스트림 2: 패킷 C ✗ (손실)
스트림 3: 패킷 D ✓, 패킷 E ✓
→ 스트림 2의 패킷 C 재전송을 기다리는 동안
스트림 1과 스트림 3은 계속 진행!
연결 마이그레이션 (Connection Migration)
TCP는 IP 주소와 포트 기반으로 연결을 식별합니다. Wi-Fi에서 LTE로 전환되면 연결이 끊깁니다. QUIC는 Connection ID로 연결을 식별하므로 네트워크가 바뀌어도 연결이 유지됩니다.
TCP: Wi-Fi 연결 → LTE 전환 → IP 변경 → 연결 끊김 → 재연결 필요
QUIC: Wi-Fi 연결 → LTE 전환 → IP 변경 → Connection ID로 연결 유지
세 버전 종합 비교
| 항목 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 표준화 연도 | 1997 | 2015 | 2022 |
| 전송 프로토콜 | TCP | TCP | UDP (QUIC) |
| 데이터 형식 | 텍스트 | 바이너리 | 바이너리 |
| 멀티플렉싱 | 없음 | 있음 (TCP HOL 남음) | 있음 (완전 해결) |
| 헤더 압축 | 없음 | HPACK | QPACK |
| 서버 푸시 | 없음 | 있음 | 없음 (제거됨) |
| TLS | 선택 | 선택 (실질적 필수) | 내장 필수 |
| 연결 수립 | 3.5 RTT | 2.5 RTT | 0-1 RTT |
| 연결 마이그레이션 | 없음 | 없음 | 있음 |
| 브라우저 지원 | 전체 | 전체 | 약 95% |
실무에서의 HTTP 버전 확인
// Next.js / Node.js에서 HTTP 버전 확인
// HTTP/2는 next.js 자동 지원 (HTTPS 환경에서)
// 브라우저 Network 탭 → Protocol 컬럼
// h1: HTTP/1.1
// h2: HTTP/2
// h3: HTTP/3
// curl로 확인
// curl -I --http2 https://example.com
// curl --http3 https://example.com
nginx에서 HTTP/2 활성화
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/example.crt;
ssl_certificate_key /etc/ssl/example.key;
ssl_protocols TLSv1.2 TLSv1.3;
}
nginx에서 HTTP/3 활성화
server {
listen 443 ssl;
listen 443 quic reuseport; # HTTP/3
ssl_certificate /etc/ssl/example.crt;
ssl_certificate_key /etc/ssl/example.key;
add_header Alt-Svc 'h3=":443"; ma=86400'; # HTTP/3 광고
}
정리
HTTP/1.1은 텍스트 기반으로 간단하지만 HOL 블로킹과 헤더 오버헤드가 문제입니다. HTTP/2는 바이너리 멀티플렉싱과 헤더 압축으로 성능을 크게 향상시켰지만 TCP HOL 블로킹은 남았습니다. HTTP/3는 QUIC 프로토콜로 TCP의 한계를 근본적으로 해결하고, 불안정한 네트워크에서도 빠른 성능을 제공합니다.