이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • HTTP/1.1, HTTP/2, HTTP/3가 연결 관리, multiplexing, framing, 전송 계층에서 어떻게 다른가?
  • CDN/LB/Gateway가 front에서는 HTTP/2/3, upstream은 HTTP/1.1로 바꿀 때 어떤 문제가 생기는가?
  • gRPC, streaming, header size, flow control, UDP 443 장애를 어떻게 진단하는가?

개요

HTTP 버전은 문법 차이만이 아니라 연결을 쓰는 방식의 차이다. HTTP/1.1은 연결 단위로 요청을 처리하고, HTTP/2는 한 TCP 연결에서 여러 stream을 multiplexing하며, HTTP/3는 QUIC/UDP 위에서 동작한다.

  • HTTP/1.1은 연결 재사용, HTTP/2는 한 연결 multiplexing, HTTP/3는 QUIC 기반 전송을 사용한다.
  • 버전 차이는 proxy, LB, client library 호환성에 영향을 준다.
  • 백엔드는 protocol upgrade와 downgrade 구간을 관찰할 수 있어야 한다.

왜 백엔드 개발자가 알아야 하는가

운영 환경에서는 client가 보는 HTTP 버전과 origin app이 받는 HTTP 버전이 다를 수 있다. CDN은 client와 HTTP/3로 통신하고 origin에는 HTTP/1.1로 요청할 수 있다. 이 차이를 모르면 protocol 문제를 application logic 문제로 오해한다.

  • gRPC는 HTTP/2가 필요하다. 중간 proxy가 HTTP/1.1만 지원하면 실패한다.
  • HTTP/2 stream limit이나 flow control 문제는 단순 thread 부족처럼 보일 수 있다.
  • HTTP/3는 UDP 443이 막히면 h2/h1 fallback이 일어나거나 특정 네트워크에서만 느려질 수 있다.
  • header compression과 큰 header 제한은 gateway별로 다르게 터진다.
  • WebSocket, SSE, streaming은 protocol 변환과 buffering에 민감하다.

요청 흐름에서의 위치

HTTP 버전은 client-edge-origin 사이에서 여러 번 바뀔 수 있다.

Browser --HTTP/3--> CDN
CDN     --HTTP/2--> Gateway
Gateway --HTTP/1.1--> Spring Boot app
App     --HTTP/2--> gRPC dependency

장애 보고에는 사용자가 본 protocol만이 아니라 각 구간의 protocol과 변환 지점을 적어야 한다.

원리

버전별 핵심 차이는 다음과 같다.

버전전송특징실무 포인트
HTTP/1.1TCPtext, keep-alive, request/response 순서connection pool, head-of-line, chunked
HTTP/2TCPbinary frame, multiplexing, HPACKstream limit, flow control, gRPC
HTTP/3UDP/QUICQUIC stream, TLS 1.3 통합UDP 443, edge support, fallback

ALPN은 TLS handshake에서 어떤 application protocol을 쓸지 협상한다. h2, http/1.1 같은 값이 여기서 결정된다. HTTP/3는 QUIC 위에서 별도로 협상되고 Alt-Svc header를 통해 광고되기도 한다.

핵심 판단 기준

  • client-facing protocol과 upstream protocol을 분리해서 문서화한다.
  • gRPC는 h2 end-to-end 또는 적절한 gateway translation이 필요하다.
  • HTTP/2 multiplexing은 connection 수를 줄이지만 한 connection의 flow control이나 stream limit이 병목이 될 수 있다.
  • HTTP/3는 켜는 것보다 fallback과 observability가 중요하다.
  • version별 error rate, latency, reset, stream reset을 따로 볼 수 있어야 한다.

실무에서 자주 만나는 문제

  • HTTP/2 front와 HTTP/1.1 backend 사이에서 header 크기와 timeout 문제가 난다.
  • gRPC를 HTTP/1.1 proxy 뒤에 두어 실패한다.
  • HTTP/3 UDP 차단을 앱 장애로 본다.
  • CDN은 h3로 정상인데 origin h1 upstream connection pool이 고갈된다.
  • h2 stream 수 제한에 걸려 client는 한 연결에서 대기한다.
  • header 이름 대소문자 처리나 pseudo-header를 잘못 다뤄 gateway에서 400이 난다.
  • h2c를 지원하지 않는 proxy 뒤에 cleartext gRPC를 붙인다.
  • HTTP/2를 켜고도 upstream이 HTTP/1.1이라 기대한 latency 개선이 나오지 않는다.

HTTP 버전 문제는 “브라우저는 h3, app log는 h1”처럼 계층마다 다른 표정을 가진다. 같은 요청의 protocol을 구간별로 기록하지 않으면 원인 후보가 흐려진다.

디버깅 방법

  • curl --http1.1, --http2, --http3로 client-facing protocol을 나눠 본다.
  • TLS ALPN 결과를 openssl s_client -alpn으로 확인한다.
  • 브라우저 DevTools의 Protocol column을 켠다.
  • CDN/LB/Gateway 로그에서 downstream protocol과 upstream protocol을 분리해 본다.
  • gRPC 장애는 h2 지원 여부, content-type, stream reset code를 확인한다.
curl --http1.1 -I https://api.example.com/health
curl --http2 -I https://api.example.com/health
curl --http3 -I https://api.example.com/health
 
openssl s_client -connect api.example.com:443 -servername api.example.com -alpn 'h2,http/1.1' </dev/null 2>/dev/null \
  | grep -i 'ALPN protocol'

h3만 실패하면 UDP/edge/fallback을, h2만 실패하면 ALPN/proxy h2 설정/stream limit을, 모든 버전이 실패하면 DNS/TCP/TLS/app까지 넓혀 본다.

코드로 확인하기

curl timing에 protocol을 함께 남기면 원인 분리가 쉬워진다.

for VERSION in --http1.1 --http2; do
  curl "$VERSION" -sS -o /dev/null \
    -w "version=${VERSION} code=%{http_code} remote_ip=%{remote_ip} connect=%{time_connect} tls=%{time_appconnect} first_byte=%{time_starttransfer} total=%{time_total}\n" \
    https://api.example.com/health
done

curl --http3는 환경에 따라 지원되지 않을 수 있다. 운영 smoke test에서는 지원 여부를 명시하고, 대체로 CDN log나 browser DevTools를 사용한다.

주니어가 자주 하는 오해

  • HTTP/2면 모든 요청이 무조건 빨라진다고 생각한다.
    • multiplexing은 장점이지만 flow control, stream limit, upstream 병목은 남는다.
  • 사용자가 h3로 접속하면 app도 h3를 받는다고 생각한다.
    • CDN/LB가 protocol을 변환할 수 있다.
  • gRPC도 일반 REST처럼 아무 proxy 뒤에 두면 된다고 생각한다.
    • gRPC는 HTTP/2와 streaming 특성 때문에 proxy 지원 여부가 중요하다.
  • h3 장애는 app 로그에서 찾을 수 있다고 생각한다.
    • h3 문제는 edge나 UDP 경로에서 끝나 app에 도달하지 않을 수 있다.

시니어의 설계 판단 기준

  • protocol 선택은 client 다양성, proxy 지원, streaming/gRPC 여부, observability 성숙도로 결정한다.
  • front protocol과 upstream protocol이 다르면 변환 계층의 limit과 log를 먼저 설계한다.
  • h2/h3는 version별 fallback 전략과 metric 없이는 켜지 않는다.
  • gRPC endpoint와 REST endpoint를 같은 ingress rule에 섞을 때 protocol 요구사항을 검증한다.
  • latency 개선을 기대한다면 version 변경 전후로 DNS/connect/TLS/TTFB/total을 나눠 측정한다.

인프라 협업 포인트

  • CDN/LB에 downstream protocol, upstream protocol, ALPN, h2/h3 enable 여부를 요청서에 포함한다.
  • HTTP/3는 UDP 443, WAF 지원, edge metric, fallback 정책을 함께 확인한다.
  • gRPC는 h2 지원, timeout, max message size, stream reset log를 함께 본다.
  • 인프라 담당자는 protocol을 켜는 일보다 각 구간의 호환성과 rollback을 검증하는 일이 더 어렵다.

실전 팁

  • HTTP/2/3 적용 전후에는 version별 latency histogram을 따로 본다.
  • gRPC는 ingress 선택 단계에서 h2 지원 여부를 먼저 확인한다.
  • h3 rollout은 일부 domain/path/user-agent에서 시작해 fallback을 관찰한다.
  • “protocol mismatch”는 400/502/stream reset처럼 여러 형태로 보일 수 있다.

위험 신호!

  • client-facing protocol과 upstream protocol을 아무도 모른다.
  • gRPC가 HTTP/1.1 proxy 뒤에 있다.
  • HTTP/3를 켰지만 UDP 443 실패율과 fallback rate를 보지 않는다.
  • h2 stream reset이 늘었는데 HTTP status만 본다.
  • protocol 변경 후 rollback 방법이 없다.

확인 질문

확인 질문

  • client가 HTTP/3로 접속해도 app은 HTTP/1.1을 받을 수 있는 이유는 무엇인가?
    • CDN/LB/Gateway가 client-facing protocol과 origin upstream protocol을 다르게 설정하고 변환할 수 있기 때문이다.
  • gRPC를 배포할 때 HTTP 버전에서 가장 먼저 확인할 것은 무엇인가?
    • client부터 ingress, gateway, upstream까지 HTTP/2를 지원하거나 적절한 protocol translation이 있는지 확인해야 한다.
  • HTTP/3 장애를 앱 장애와 구분하려면 무엇을 봐야 하는가?
    • UDP 443, CDN/edge log, browser protocol, h2 fallback 성공 여부, origin app log 도달 여부를 봐야 한다.

참고 문서