HTTP/1.1 vs HTTP/2 vs HTTP/3: 차이와 적용 방법
멀티플렉싱·헤더 압축·QUIC까지 버전별 차이를 정리하고 내 서버에 적용·확인하는 방법.
같은 HTML·CSS·이미지를 내려받아도 HTTP 버전에 따라 페이지가 뜨는 속도는 크게 달라집니다. HTTP/1.1은 1997년 설계 그대로 요청을 한 줄로 세워 처리하고, HTTP/2는 하나의 연결에서 수십 개 요청을 동시에 실어 나르며, HTTP/3는 아예 TCP를 버리고 QUIC(UDP 기반)으로 갈아탔습니다. 이 글은 세 버전이 정확히 무엇이 다른지, 왜 달라졌는지, 그리고 내 서버에 HTTP/2·HTTP/3를 켜고 확인하는 방법까지 정리합니다.
지금 내 사이트가 어떤 버전으로 응답하는지 궁금하다면 먼저 HTTP/2·HTTP/3 지원 확인 도구로 도메인을 넣어보세요. 결과를 보고 나서 아래 내용을 읽으면 훨씬 빠르게 이해됩니다.
HTTP/1.1의 세 가지 한계
HTTP/1.1이 느린 이유는 크게 세 가지입니다.
- 연결당 요청 1개 + 브라우저 연결 6개 제한 — HTTP/1.1은 하나의 TCP 연결에서 한 번에 요청 하나만 처리합니다(응답이 와야 다음 요청). 그래서 브라우저는 도메인당 TCP 연결을 6개까지 열어 병렬화하지만, 리소스가 수십 개인 현대 페이지에서는 턱없이 부족합니다.
- HOL(Head-of-Line) 블로킹 — 앞선 요청의 응답이 느리면(예: 큰 이미지) 같은 연결의 뒤 요청들이 전부 대기합니다. 파이프라이닝이 스펙에는 있었지만 이 문제 때문에 사실상 아무 브라우저도 쓰지 않았습니다.
- 텍스트 헤더의 반복 전송 —
Cookie,User-Agent같은 헤더가 요청마다 평문으로 통째로 다시 전송됩니다. 쿠키가 큰 사이트라면 요청 하나에 수 KB씩 낭비됩니다.
이 한계를 우회하려고 나온 편법이 도메인 샤딩(정적 자원을 img1.example.com, img2.example.com으로 쪼개 연결 수를 늘리기), CSS 스프라이트, JS/CSS 통합 번들이었습니다. 뒤에서 다루지만 이 기법들은 HTTP/2에서는 오히려 성능을 해치는 안티패턴이 됩니다.
HTTP/2 — 하나의 TCP 연결, 여러 스트림
2015년 표준화된 HTTP/2의 핵심 변화는 네 가지입니다.
- 바이너리 프레이밍 — 텍스트 프로토콜을 버리고 요청·응답을 작은 바이너리 프레임으로 쪼갭니다. 파싱이 빠르고 모호함이 없습니다.
- 멀티플렉싱 — 하나의 TCP 연결 위에 여러 스트림을 만들어 요청 수십 개를 동시에 주고받습니다. 연결 6개 제한, 요청 줄 세우기가 한 번에 해결됩니다.
- HPACK 헤더 압축 — 이미 보낸 헤더는 인덱스 번호로 대체합니다. 두 번째 요청부터는 쿠키·UA를 다시 보내지 않아 헤더 크기가 수 KB에서 수십 바이트로 줄어듭니다.
- 우선순위 지정 — CSS·폰트처럼 렌더링을 막는 리소스를 이미지보다 먼저 받도록 스트림에 가중치를 줄 수 있습니다.
다만 남은 약점이 있습니다. 스트림은 여러 개지만 TCP 연결은 하나라서, 패킷 하나가 유실되면 TCP가 그 패킷을 재전송받을 때까지 뒤에 도착한 모든 스트림의 데이터가 커널 버퍼에서 대기합니다. 애플리케이션 계층의 HOL 블로킹은 없앴지만 전송(TCP) 계층의 HOL 블로킹은 그대로라는 뜻입니다. 유선 환경에서는 문제가 안 되지만, 패킷 손실이 잦은 모바일 망에서는 HTTP/2가 HTTP/1.1(연결 6개가 서로 독립)보다 오히려 느려지는 역전도 생깁니다.
HTTP/3 — QUIC이 TCP를 대체하다
HTTP/3(2022년 RFC 9114)는 이 마지막 문제를 풀기 위해 전송 계층 자체를 바꿨습니다. TCP 대신 UDP 위에 구현된 QUIC을 사용합니다.
- 스트림별 독립 손실 복구 — QUIC은 스트림을 전송 계층에서 이해합니다. 패킷이 유실되면 그 패킷에 실린 스트림만 기다리고 나머지 스트림은 그대로 진행합니다. TCP HOL 블로킹이 사라집니다.
- 핸드셰이크 단축과 0-RTT — TCP+TLS 1.3은 첫 연결에 왕복 2회(RTT)가 필요하지만 QUIC은 전송·암호화 협상을 합쳐 1-RTT로 끝냅니다. 재방문이라면 이전 세션 정보로 0-RTT, 즉 첫 패킷에 요청을 실어 보낼 수 있습니다.
- 연결 마이그레이션 — TCP 연결은 IP:포트 4튜플로 식별되어 와이파이에서 LTE로 바뀌면 끊어지지만, QUIC은 연결 ID로 식별하므로 네트워크가 바뀌어도 다운로드가 이어집니다.
- QPACK 헤더 압축 — HPACK을 QUIC의 순서 독립 스트림에 맞게 고친 버전입니다.
비교표와 실전 예시: 리소스 30개 페이지
| 항목 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 전송 계층 | TCP | TCP | QUIC (UDP) |
| 멀티플렉싱 | 없음 (연결 6개로 우회) | 스트림 다중화 | 스트림 다중화 |
| HOL 블로킹 | 요청 단위 (심함) | TCP 계층에 잔존 | 사실상 없음 |
| 첫 연결 핸드셰이크 | 2~3 RTT (TCP+TLS) | 2 RTT (TCP+TLS 1.3) | 1 RTT, 재방문 0-RTT |
| 헤더 압축 | 없음 (평문) | HPACK | QPACK |
| 네트워크 전환(WiFi→LTE) | 연결 끊김 | 연결 끊김 | 연결 유지 (마이그레이션) |
예시 계산 — CSS 2개, JS 5개, 이미지 23개, 총 30개 리소스를 RTT 50ms 환경에서 받는다고 해봅시다. HTTP/1.1에서는 연결 6개로 나눠 받으므로 최소 5배치(30÷6)가 순차 진행되고, 배치마다 최소 1 RTT씩 걸려 리소스 대기만 250ms 이상, 여기에 연결 6개 각각의 TCP+TLS 핸드셰이크 비용이 더해집니다. HTTP/2에서는 이미 열린 연결 하나로 30개 요청을 한 번에 보내고 응답이 병렬로 흘러들어오므로, 이론상 가장 큰 리소스 하나의 다운로드 시간 + 1 RTT 수준으로 줄어듭니다. 실제 차이는 응답 속도 측정 도구로 같은 URL의 TTFB·전체 응답 시간을 재보면 체감할 수 있습니다.
내 서버에 켜는 법과 확인 방법
HTTP/2 켜기 (nginx) — 사실상 HTTPS가 전제 조건입니다(브라우저가 h2를 TLS에서만 협상).
- 구버전 nginx:
listen 443 ssl http2; - nginx 1.25.1 이상:
listen 443 ssl;아래에http2 on;(listen 플래그 방식은 deprecated) - Apache는
Protocols h2 http/1.1, 대부분의 CDN(Cloudflare 등)은 기본 활성화
HTTP/3 켜기 — 조건이 조금 더 까다롭습니다.
- nginx 1.25 이상(QUIC 지원 빌드) 또는 HTTP/3를 지원하는 CDN 사용
listen 443 quic reuseport;+http3 on;추가 (기존listen 443 ssl;과 병행)- 브라우저에게 h3를 광고하는
Alt-Svc응답 헤더 추가:add_header Alt-Svc 'h3=":443"; ma=86400'; - 방화벽·보안그룹에서 UDP 443 인바운드 오픈 — 가장 흔한 실수가 TCP 443만 열어두는 것
브라우저는 첫 접속은 HTTP/2로 하고, 응답의 Alt-Svc 헤더를 보고 다음 요청부터 HTTP/3로 전환합니다. 이 헤더가 실제로 내려오는지는 HTTP 헤더 조회 도구로 확인하고, 최종적으로 프로토콜 협상이 되는지는 HTTP/2·HTTP/3 지원 확인으로 검증하세요.
HTTP/2 시대의 안티패턴 정리 — 1.1 시절 최적화는 이제 역효과입니다.
- 도메인 샤딩 — 연결이 하나면 충분한데 도메인을 쪼개면 DNS 조회·TCP/TLS 핸드셰이크만 늘고 HPACK 압축 문맥도 도메인별로 갈라집니다. 자원을 한 도메인으로 모으세요.
- 거대 번들·스프라이트 — 파일 하나에 몰아넣으면 아이콘 하나만 바뀌어도 전체 캐시가 무효화됩니다. 멀티플렉싱 덕에 작은 파일 여러 개의 요청 비용이 거의 없으므로, 적당한 단위로 나누고 캐시 수명을 길게 가져가는 편이 낫습니다.
- 인라인 몰아넣기 — Critical CSS 정도는 유효하지만, 캐시 가능한 자원까지 HTML에 인라인하면 재방문 성능을 잃습니다.
자주 묻는 질문
HTTP/2만 적용해도 충분한가요, HTTP/3까지 가야 하나요?
HTTP/2는 반드시 HTTPS여야 하나요?
HTTP/3를 켰는데 브라우저가 계속 h2로 접속합니다. 왜죠?
HTTP/2로 바꾸면 기존 도메인 샤딩은 어떻게 처리해야 하나요?
서버 푸시(Server Push)는 써야 하나요?
내 사이트가 어떤 HTTP 버전을 쓰는지 코드는 어떻게 확인하나요?
이 가이드와 함께 쓰면 좋은 도구
관련 가이드
- Core Web Vitals(LCP·CLS·INP) 개선 체크리스트LCP·CLS·INP가 무엇이고 무엇을 측정하는지, 점수를 올리는 실전 체크리스트.
- 301 vs 302 vs 307/308: 올바른 리다이렉트리다이렉트 상태코드의 차이와 SEO·메서드 보존 관점에서 올바르게 고르는 법.