HTTP/2와 HTTP/3, 성능을 바꾸는 경계
HTTP/2와 HTTP/3가 요청 병렬성, 핸드셰이크, 헤더 압축, 패킷 손실 회복 방식을 어떻게 바꾸는지 설명한다. gRPC, ALB, Nginx, DevTools 디버깅에서 어떤 판단 기준이 필요한지도 함께 짚는다.
Script Companion
오디오와 함께 스크립트 보기
- 01
HTTP 버전은 단순히 숫자가 올라간 프로토콜 표기가 아니다. 요청을 얼마나 병렬로 보낼 수 있는지, 핸드셰이크에 몇 번의 왕복 지연이 필요한지, 헤더 중복을 어떻게 줄이는지, 패킷 손실이 났을 때 어디까지 영향이 퍼지는지를 바꾼다. 그래서 Nginx에서 HTTP/2를 켰는지보다 중요한 질문은 따로 있다. Protocol 컬럼이 실제로 h2인지, Waiting for server response가 어떻게 변했는지, 재전송률과 연결 수가 함께 줄었는지를 봐야 한다.
- 02
HTTP/2는 2015년 RFC 7540으로 표준화됐고, 기반에는 Google의 SPDY 프로토콜이 있다. HTTP/1.1이 사람이 읽기 쉬운 텍스트 기반이었다면 HTTP/2는 바이너리 프레임 기반으로 통신을 나눈다. HEADERS는 요청과 응답의 시작이 되는 헤더를, DATA는 본문을, SETTINGS는 커넥션 설정을, WINDOW_UPDATE는 흐름 제어를 다룬다. PING, RST_STREAM, GOAWAY 같은 프레임도 커넥션 유지, 스트림 종료, 커넥션 종료 알림에 쓰인다.
- 03
HTTP/2의 중심에는 스트림과 멀티플렉싱이 있다. 스트림은 하나의 TCP 커넥션 안에서 독립적으로 흐르는 양방향 바이트 흐름이고, 각 스트림은 고유한 ID를 가진다. 클라이언트는 홀수 스트림 ID를 쓰고 서버 푸시는 짝수 스트림 ID를 쓴다. 이 구조 덕분에 HTTP/1.1에서 6개의 TCP 커넥션이 필요했던 작업을 HTTP/2는 1개의 TCP 커넥션으로 처리할 수 있다. TCP 핸드셰이크 비용이 한 번으로 줄어드는 것이 직접적인 효과다.
- 04
하지만 멀티플렉싱이 모든 성능 문제를 없애지는 않는다. HTTP/2에는 스트림 간 의존성과 가중치를 두는 우선순위 메커니즘이 있고, 브라우저가 보통 자동으로 우선순위를 설정한다. 다만 RFC 9113은 RFC 7540의 tree 기반 priority signaling을 deprecated 처리했다. 그래서 LCP 개선을 priority weight 하나에 기대하면 안 된다. CSS보다 대형 이미지가 먼저 대역폭을 점유한다면 preload, critical CSS, 이미지 lazy loading 같은 리소스 힌트와 번들 전략을 먼저 조정해야 한다.
- 05
HTTP/3의 핵심 차이는 전송 계층이 TCP가 아니라 UDP 기반 QUIC이라는 점이다. HTTP/2는 애플리케이션 레벨의 HOL Blocking은 줄이지만 TCP 레벨 HOL Blocking은 남는다. 반면 HTTP/3는 QUIC 위에서 동작해 TCP 레벨 HOL Blocking을 피하고, 연결 수립도 1 RTT 또는 0-RTT로 줄어들 수 있다. 표로 정리하면 HTTP/2는 바이너리 프레임, HPACK, TCP를 쓰고, HTTP/3는 QUIC 기반 바이너리 전송, QPACK, UDP, Connection ID, TLS 1.3 내장을 특징으로 한다.
- 06
HTTP/3가 특히 효과적인 곳은 모바일처럼 Wi-Fi와 LTE 전환이 잦은 환경, 고지연이나 패킷 손실이 있는 네트워크, 그리고 많은 소규모 병렬 요청이 있는 경우다. 반대로 안정적인 유선 네트워크 내부 통신, 데이터센터 내부 통신, UDP 443을 차단하는 일부 기업 네트워크에서는 HTTP/2가 여전히 좋은 선택일 수 있다. gRPC 내부 통신도 HTTP/2 기반이 안정적이다. 결정 기준은 최신 버전이냐가 아니라 실패했을 때 HTTP/2로 자연스럽게 돌아오는가다.
- 07
운영에서 HTTP/3를 켤 때는 Alt-Svc: h3=":443"; ma=... 같은 광고와 HTTP/2 fallback이 함께 살아 있어야 한다. RFC 9114는 UDP 차단 같은 연결 문제가 있으면 TCP 기반 HTTP 버전을 시도해야 한다고 권고한다. 이 조건이 깨지면 사용자는 일부 네트워크에서만 느리거나 접속이 안 된다고 느끼지만, 서버 로그에는 정상 HTTP/2 요청만 남을 수 있다. 이런 silent failure를 피하려면 HTTP/3를 단독 경로로 만들지 말고 TCP 443 listener와 HTTP/2 fallback을 유지한 채 QUIC 실패율을 별도 지표로 봐야 한다.
- 08
트러블슈팅에서는 증상보다 경로를 먼저 확인해야 한다. gRPC 요청이 ALB에서 502 Bad Gateway를 반환한다면, ALB 타겟 그룹의 Protocol Version이 HTTP1로 설정되어 gRPC 요청이 백엔드로 HTTP/1.1 다운그레이드됐을 가능성이 있다. HTTP/2 활성화 뒤 Nginx에서 ERR_HTTP2_PROTOCOL_ERROR가 난다면 대용량 다운로드나 SSE 스트리밍에서 proxy_buffer_size, http2_chunk_size, Keep-Alive 단절, Transfer-Encoding: chunked 같은 HTTP/1.1 전용 헤더를 의심한다.
- 09
정리하면 HTTP/2는 HTTP/1.1 시대의 파일 수 줄이기 패턴을 흔들었다. 단일 TCP 커넥션 안의 스트림과 멀티플렉싱, 바이너리 프레임, HPACK이 핵심이다. HTTP/3는 TCP가 만능이 아니라는 점을 보여주며 UDP 기반 QUIC, QPACK, Connection ID, TLS 1.3 내장으로 다른 경계를 만든다. gRPC를 쓴다면 HTTP/2의 스트림과 멀티플렉싱을 이해해야 하고, ALB에서 end-to-end HTTP/2가 필요하면 GRPC 타겟 그룹 타입을 써야 한다. 성능 분석의 출발점은 DevTools의 Protocol 컬럼과 Waterfall이다.
같은 레이어
L7에서 이어 듣기
- 오디오 파일
- /podcasts/l7-http2-http3.mp3