Theory
네트워크
HTTP 버전 비교 (1.1, 2, 3)

HTTP 버전의 진화: 1.1에서 3.0까지

웹의 발전과 함께 HTTP 프로토콜은 성능 병목을 해결하기 위해 지속적으로 진화해왔습니다. 이 문서는 HTTP/1.1의 한계점부터 HTTP/2의 멀티플렉싱 혁신, 그리고 HTTP/3(QUIC)의 전송 계층 혁명까지의 과정을 문제 해결(Problem-Solution) 관점에서 상세히 다룹니다.

1. HTTP/1.1: 표준의 정립과 한계

HTTP/1.1은 오랫동안 웹의 표준이었지만, 웹 페이지가 복잡해짐에 따라 구조적인 한계가 드러났습니다.

1.1 구조적 특징

  • 텍스트 기반(Text-based): 사람이 읽을 수 있는 텍스트 형식으로 데이터를 전송합니다.
  • 요청-응답 모델: 클라이언트가 요청을 보내면 서버가 응답하는 단순한 구조입니다.

1.2 주요 문제점: 2단계 HOL Blocking

HTTP/1.1은 두 가지 계층에서 HOL(Head-of-Line) Blocking 문제를 겪습니다.

간단 정리

HTTP/1.1의 문제 (애플리케이션 계층):

  • 서버가 응답을 요청받은 순서대로만 보냄
  • 첫 번째 파일 처리가 느리면 → 이미 준비된 나머지 파일도 전부 대기

HTTP/1.1 & HTTP/2의 공통 문제 (TCP 계층):

  • TCP는 "파일"을 모르고 "패킷 번호"만 관리함
    • HTTP/2가 파일 C, A, B 순서로 전송해도
    • TCP 패킷은 섞여서 전송됨: [C][C][A][A][B][B]... → 각각 패킷 #1, #2, #3, #4, #5, #6
  • 어느 파일의 패킷이든 하나라도 손실되면 전체 블로킹
    • 패킷 #3(파일 A)이 손실되면 → #4, #5, #6이 도착해도 TCP가 HTTP/2에 안 줌
    • 파일 B의 패킷(#5, #6)도 이미 도착했지만 #3이 재전송될 때까지 대기

1.2.1 애플리케이션 계층 HOL Blocking (HTTP 프로토콜 설계)

HTTP/1.1 프로토콜 자체가 응답 순서를 요청 순서와 동일하게 유지하도록 설계되었습니다.

  • 상황: 브라우저가 하나의 TCP 연결에서 style.css, script.js, image.jpg를 순서대로 요청합니다.
  • 문제: style.css서버 처리가 지연되면, 이미 준비된 script.jsimage.jpg도 전송되지 못하고 대기합니다.
  • 원인: HTTP/1.1은 파이프라이닝을 지원하지만, 응답은 반드시 요청 순서대로 전송되어야 합니다. (RFC 7230 Section 6.3.2 (opens in a new tab))
  • 임시방편: 브라우저는 도메인당 6개의 TCP 연결을 동시에 사용(Domain Sharding)하여 우회했지만, 서버 리소스 낭비를 유발합니다.

1.2.2 TCP 계층 HOL Blocking (전송 계층)

HTTP/1.1은 TCP 위에서 동작하므로, TCP의 패킷 순서 보장 메커니즘에서 발생하는 HOL Blocking도 겪습니다.

  • 문제: TCP는 패킷 손실 시 재전송이 완료될 때까지 이후 모든 데이터를 애플리케이션에 전달하지 않습니다.
  • 영향: 하나의 패킷 손실이 전체 연결을 차단합니다.
  • 상세 설명: TCP 계층 HOL Blocking의 구체적인 동작 원리는 섹션 2.3에서 다룹니다.

1.2.3 HTTP 버전별 HOL Blocking 비교

HTTP 버전애플리케이션 계층 HOLTCP 계층 HOL비고
HTTP/1.1✅ 있음✅ 있음두 계층 모두에서 차단 발생
HTTP/2❌ 해결✅ 있음멀티플렉싱으로 HTTP 계층만 해결
HTTP/3❌ 해결❌ 해결QUIC(UDP)으로 두 계층 모두 해결

Salesforce Engineering: "HTTP/2 only solved HOL blocking at the HTTP level, what we might call 'Application Layer' HOL blocking. The underlying TCP connection still suffers from HOL blocking at the transport layer." (출처 (opens in a new tab))

1.3 HTTP/1.1에서의 WebSocket (RFC 6455)

결론: TCP 소켓은 유지되지만 애플리케이션 프로토콜이 HTTP에서 WebSocket으로 전환됩니다

HTTP/1.1에서 실시간 양방향 통신을 위해 WebSocket을 사용할 때는 HTTP Upgrade 메커니즘을 사용합니다. RFC 6455 - WebSocket Protocol (opens in a new tab)

MDN에서 "By far, the most common use case for upgrading an HTTP connection is to use WebSockets, which are always implemented by upgrading an HTTP or HTTPS connection" 이라고 명시되어 있습니다. MDN - Protocol upgrade mechanism (opens in a new tab)

핸드셰이크 과정

// Client Request
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
 
// Server Response
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

프로토콜 전환 메커니즘: "연결 전체가 전환된다"의 정확한 의미

101 Switching Protocols 응답 이후 정확히 무슨 일이 일어나는가? MDN - 101 Switching Protocols (opens in a new tab)

  1. TCP 연결은 유지됩니다:
    • TCP 소켓 자체는 끊어지지 않고 계속 열려있습니다
    • IP 주소, 포트 번호, 연결 상태 모두 그대로 유지됩니다
    • "Once the 101 response is received, the client and server transition to the new protocol without breaking the connection"

MDN에서 "the TCP connection is about to be used for a different protocol" 라고 명시되어 있습니다.

  1. 애플리케이션 프로토콜이 완전히 바뀝니다:
    • Before 101: HTTP 텍스트 기반 요청-응답 형식
    • After 101: WebSocket 바이너리 프레임 형식
    • RFC 6455에서 "From that point forward, only WebSocket frames flow across the connection—no HTTP framing exists"

RFC 6455에서 "the server considers the WebSocket connection to be established and that the WebSocket connection is in the OPEN state. At this point, the server may begin sending (and receiving) data" 라고 명시되어 있습니다.

  1. 전환 시점:
    • 서버: 101 응답 전송을 완료한 직후
    • 클라이언트: 101 응답 검증을 완료한 직후
    • 이 순간부터 WebSocket 연결이 OPEN 상태가 됩니다

WebSocket 프레임 구조

101 응답 이후에는 HTTP 형식이 아닌 WebSocket 바이너리 프레임 형식으로 데이터를 주고받습니다:

Opcode 값:

  • 0x1: 텍스트 프레임
  • 0x2: 바이너리 프레임
  • 0x8: 연결 종료 (Close)
  • 0x9: Ping
  • 0xA: Pong

RFC 6455에서 "a client MUST mask all frames that it sends to the server" 라고 명시되어 있습니다. 클라이언트가 보내는 모든 프레임은 마스킹되어야 합니다.

통신 방식 비교: HTTP vs WebSocket

핵심 차이:

  • HTTP (Before 101): 텍스트 기반, 요청-응답, 반이중
  • WebSocket (After 101): 바이너리 프레임, 양방향, 전이중

한계: 왜 일반 HTTP 요청을 처리할 수 없는가?

한 번 WebSocket으로 전환된 연결은 더 이상 일반 HTTP 요청을 처리할 수 없습니다. 그 이유는:

  1. 프레임 형식 불일치:

    • HTTP는 텍스트 기반: GET /api HTTP/1.1\r\n...
    • WebSocket은 바이너리: [FIN|RSV|Opcode|Mask|Len|Key|Data]
    • 서버의 HTTP 파서는 WebSocket 프레임을 이해할 수 없음
  2. 통신 모델 차이:

    • HTTP: 클라이언트 요청 → 서버 응답 (단방향 사이클)
    • WebSocket: 양방향 독립적 메시지 (누구든지 언제든지 전송 가능)
  3. 프로토콜 상태:

    • WebSocket OPEN 상태에서는 WebSocket 프레임만 허용
    • HTTP 메시지를 보내면 프로토콜 위반으로 연결 종료

MDN에서 "Right after sending the 101 status code, the server can begin speaking the new protocol" 라고 명시되어 있으며, 이는 HTTP를 더 이상 사용하지 않음을 의미합니다.

결론: TCP 연결은 물리적으로 유지되지만, 그 위에서 "말하는 언어(프로토콜)"가 HTTP에서 WebSocket으로 완전히 바뀝니다. 따라서 HTTP/1.1의 WebSocket은 연결 독점이라는 한계를 가지며, 이것이 HTTP/2의 Extended CONNECT가 필요한 이유입니다.


2. HTTP/2: 멀티플렉싱 혁명

HTTP/2는 HTTP/1.1의 HOL Blocking 문제를 해결하기 위해 바이너리 프레이밍(Binary Framing) 계층을 도입했습니다.

2.1 해결책: 바이너리 프레이밍과 스트림

HTTP/2는 데이터를 텍스트가 아닌 바이너리 프레임(Frame) 단위로 쪼개서 전송합니다.

  • 스트림(Stream): 가상의 채널입니다. 각 요청/응답은 고유한 Stream ID를 가집니다.
  • 프레임(Frame): 데이터의 조각입니다. HEADERS 프레임, DATA 프레임 등으로 나뉩니다.
  • 멀티플렉싱(Multiplexing): 여러 스트림의 프레임들이 하나의 TCP 연결에 뒤섞여 전송됩니다. 수신 측에서는 Stream ID를 보고 다시 조립합니다.

예시:

HTTP/1.1에서는 style.css가 막히면 뒤의 요청도 막혔지만, HTTP/2에서는 style.css의 데이터 프레임과 script.js의 데이터 프레임이 섞여서 전송되므로, 먼저 준비된 데이터가 먼저 도착할 수 있습니다.

2.2 HTTP/2에서의 WebSocket (RFC 8441)

결론: Extended CONNECT 메서드로 단일 스트림만 WebSocket 터널로 전환

HTTP/2는 하나의 TCP 연결을 공유하므로, HTTP/1.1처럼 연결 전체를 점유하는 Upgrade 방식을 사용할 수 없습니다. 대신 Extended CONNECT 메서드를 사용합니다. RFC 8441 - Bootstrapping WebSockets with HTTP/2 (opens in a new tab)

RFC 8441 Section 3에서 "Upon receipt of SETTINGS_ENABLE_CONNECT_PROTOCOL with a value of 1, a client MAY use the Extended CONNECT" 라고 명시되어 있습니다.

설정 및 요청 과정

  1. 설정 교환:

    • 클라이언트와 서버는 SETTINGS_ENABLE_CONNECT_PROTOCOL (코드 0x8, 초기값 0) 설정을 교환하여 Extended CONNECT를 활성화합니다
    • 값이 1이면 Extended CONNECT 사용 가능을 의미합니다
  2. :protocol 의사 헤더 (MUST):

    • RFC 8441 Section 5에서 명시: "The :protocol pseudo-header field MUST be included in the CONNECT request, and it MUST have a value of 'websocket'"
    • 이는 필수 요구사항으로, 생략하거나 다른 값을 사용하면 WebSocket 연결이 실패합니다

RFC 8441 Section 4에서 "A new pseudo-header field :protocol MAY be included on request HEADERS indicating the desired protocol to be spoken on the tunnel created by CONNECT" 라고 명시되어 있습니다.

HTTP/2 WebSocket 요청 예시

// HTTP/2 WebSocket Request (Stream ID: 5)
HEADERS + END_HEADERS
  :method = CONNECT
  :protocol = websocket
  :scheme = https
  :path = /chat
  :authority = server.example.com
  sec-websocket-protocol = chat, superchat
  sec-websocket-extensions = permessage-deflate
  sec-websocket-version = 13
  origin = http://www.example.com

위 예시는 RFC 8441 Section 5.1에서 제공하는 공식 예시입니다.

특징

  • 단일 스트림 점유: 특정 스트림(예: Stream 5) 하나만 웹소켓 터널로 전환됩니다
  • 트래픽 공존: 다른 스트림들은 여전히 일반 HTTP 요청을 주고받을 수 있습니다
  • 결과: 단일 TCP 연결 위에서 웹소켓과 일반 HTTP 트래픽이 공존합니다

프로토콜 전환 메커니즘: "스트림 전환"의 정확한 의미

200 OK 응답 이후 정확히 무슨 일이 일어나는가? RFC 8441 - Bootstrapping WebSockets with HTTP/2 (opens in a new tab)

  1. TCP 연결과 다른 스트림은 그대로 유지됩니다:
    • TCP 소켓은 여전히 HTTP/2 프로토콜로 동작합니다
    • 다른 스트림들(예: Stream 1, 3, 7)은 계속 일반 HTTP 요청-응답을 처리합니다
    • "피어는 CONNECT 트랜잭션의 HTTP/2 스트림을 RFC 6455에서 참조하는 TCP 연결처럼 사용"

RFC 8441에서 "a CONNECT request that has a :protocol pseudo-header field is processed by converting to WebSocket" 라고 명시되어 있습니다.

  1. 해당 스트림(예: Stream 5)만 WebSocket 터널로 전환됩니다:
    • Before 200: HTTP/2 HEADERS 프레임 (요청-응답)
    • After 200: WebSocket 바이너리 프레임이 HTTP/2 DATA 프레임 안에 캡슐화됨
    • WebSocket 프레임과 HTTP/2 프레임이 중첩 구조로 동작

RFC 8441에서 "the stream is now being used for WebSocket. The WebSocket connection is in the OPEN state" 라고 명시되어 있습니다.

  1. 전환 시점 및 상태:

    • 서버: 200 응답의 END_HEADERS 플래그 전송 완료 직후
    • 클라이언트: 200 응답 수신 및 검증 완료 직후
    • 이 순간부터 해당 스트림의 WebSocket이 OPEN 상태가 됩니다
  2. 종료 메커니즘:

    • 정상 종료: END_STREAM 플래그 (TCP FIN에 해당)
    • 비정상 종료: RST_STREAM 프레임 + CANCEL 오류 코드 (TCP RST에 해당)

RFC 8441에서 "the usual TCP-level closures are represented as END_STREAM flags, while RST exceptions are represented with RST_STREAM frames with the CANCEL error code" 라고 명시되어 있습니다.

HTTP/1.1과 HTTP/2 WebSocket 비교

특성HTTP/1.1 WebSocketHTTP/2 WebSocket
전환 방식Upgrade 헤더 + 101 응답Extended CONNECT + 200 응답
전환 범위연결 전체가 WebSocket으로 전환단일 스트림만 WebSocket으로 전환
TCP 연결HTTP → WebSocket (완전 전환)여전히 HTTP/2 유지
다른 트래픽❌ 불가능 (연결 점유)✅ 가능 (다른 스트림 사용)
핸드셰이크Sec-WebSocket-Key/Accept:protocol 의사 헤더

2.3 남겨진 문제: TCP 계층 HOL Blocking

결론: TCP의 순서 보장 특성이 HTTP/2의 멀티플렉싱을 무력화시킵니다

HTTP/2는 애플리케이션 계층에서 HOL Blocking을 해결했지만, **전송 계층(TCP)**의 HOL Blocking은 여전히 남아있습니다. HTTP/3 explained - TCP HOL (opens in a new tab)

HTTP/3 explained에서 "TCP thinks that a part of the single file has been lost, so it keeps the rest of the data from being processed until the hole is filled - while at the HTTP/2 level we know we could already process files A and C, TCP does not know this" 라고 명시되어 있습니다.

TCP HOL Blocking이란?

TCP는 바이트 스트림의 순서를 보장하는 프로토콜입니다. 네트워크에서 패킷 하나가 손실되면, 그 패킷이 재전송될 때까지 그 뒤에 도착한 모든 패킷을 애플리케이션 계층에 전달하지 않습니다. MDN - Head-of-line blocking (opens in a new tab)

MDN 문서에서 "If a segment from one stream gets lost in transmission, TCP will block everything that comes after it — including segments for completely unrelated streams" 라고 명시되어 있습니다.

구체적인 예시

3개의 파일을 HTTP/2로 다운로드하는 상황을 가정합니다: Salesforce Engineering - HTTP/2 and HOL Blocking (opens in a new tab)

단일 TCP 연결을 통한 데이터 전송: AABBCCAABBCC
- A: image1.jpg의 데이터
- B: script.js의 데이터  
- C: style.css의 데이터

문제 발생 시나리오:

  1. 전송: 서버가 세 파일의 데이터를 TCP 패킷에 섞어서 전송합니다

    • TCP Packet 1-2: AA (image1 데이터)
    • TCP Packet 3: B (script.js 데이터) ← 손실
    • TCP Packet 4-6: BCC (script.js + style.css 데이터)
    • TCP Packet 7-8: AA (image1 데이터)
    • TCP Packet 9-12: BBCC (script.js + style.css 데이터)
  2. 수신: 클라이언트가 패킷 1-2, 4-12를 모두 받았지만, 패킷 3이 손실되었습니다

  3. TCP의 동작:

    • TCP는 순서 보장을 위해 패킷 3이 재전송될 때까지 패킷 4-12를 애플리케이션(HTTP/2)에 전달하지 않습니다
    • HTTP/2 입장에서는 style.css(C)의 모든 데이터가 이미 도착했지만, TCP 계층에서 블로킹되어 사용할 수 없습니다
  4. 결과: script.js(B)의 작은 패킷 하나 때문에 완전히 무관한 style.css(C)와 image1.jpg(A)도 함께 지연됩니다

Salesforce Engineering 블로그에서 "For three example files downloaded over HTTP/2, you would get a single TCP connection, and the incoming data might look like AABBCCAABBCC. If the third TCP packet is lost (the one containing the first data for file B), but all other data is delivered, TCP retransmits a new copy... TCP thinks that a part of the single file has been lost, so it keeps the rest of the data from being processed until the hole is filled" 라고 명시되어 있습니다.

시각화: TCP HOL Blocking

성능 영향

패킷 손실률이 높아질수록 HTTP/2의 성능이 HTTP/1.1보다 나빠질 수 있습니다. Medium - Why HTTP/3 Was Invented (opens in a new tab)

위 블로그에서 "At 2% packet loss, tests have proven that HTTP/1 users are usually better off because they typically have up to six TCP connections to distribute lost packets over" 라고 명시되어 있습니다.

  • 2% 패킷 손실 환경: HTTP/1.1이 오히려 더 빠릅니다
    • HTTP/1.1은 보통 6개의 병렬 TCP 연결을 사용하므로, 손실이 여러 연결에 분산됩니다
    • HTTP/2는 단일 TCP 연결을 사용하므로, 모든 스트림이 동시에 영향을 받습니다

3. HTTP/3 (QUIC): 전송 계층의 혁명 (Deep Dive)

HTTP/3는 단순한 버전 업그레이드가 아닌, 전송 계층(Transport Layer)을 TCP에서 UDP 기반의 QUIC으로 교체한 패러다임의 전환입니다.

QUIC의 핵심: TCP HOL Blocking 완전 해결

QUIC은 UDP 위에서 스트림별 독립적인 손실 복구를 구현하여 TCP HOL Blocking을 근본적으로 해결합니다. RFC 9000 - QUIC Transport (opens in a new tab)

RFC 9000에서 "Each QUIC stream is separately flow-controlled, and lost data is retransmitted at the level of QUIC, not UDP" 라고 명시되어 있습니다.

QUIC 패킷 구조

핵심 구성 요소:

  • Packet Number: 전송 순서 추적 및 손실 감지 (전송 계층)
  • Stream ID + Offset: 데이터 순서 복원 (애플리케이션 계층)
  • Connection ID: IP 변경에도 연결 유지

3.1 User-Space 구현과 진화의 유연성

  • Kernel Bypass: TCP는 운영체제 커널(Kernel)에 구현되어 있어 프로토콜 업데이트를 위해서는 OS 업데이트가 필요했습니다. 반면, QUIC은 User-Space 위에서 동작하므로 브라우저나 애플리케이션 업데이트만으로 새로운 기능을 즉시 배포할 수 있습니다. Chromium - QUIC (opens in a new tab)
  • Middlebox Ossification 해결: 중간 장비(방화벽, 라우터)들이 TCP 헤더를 감시하고 조작하는 문제(Ossification)를 피하기 위해, QUIC은 패킷의 대부분(헤더 포함)을 암호화하여 UDP 페이로드처럼 보이게 만듭니다.

3.2 핸드셰이크(Handshake)의 통합과 최적화

TCP+TLS 구조에서는 TCP 연결(1-RTT) 후 TLS 핸드셰이크(1~2-RTT)가 순차적으로 일어납니다.

  • 1-RTT Handshake: QUIC은 전송 계층 핸드셰이크와 TLS 1.3 암호화 핸드셰이크를 단일 단계로 결합하여, 연결 수립 시간을 획기적으로 단축했습니다. IETF - QUIC Transport Draft (opens in a new tab)
  • 0-RTT (Zero Round Trip Time): 이전에 접속했던 서버와는 Session Ticket을 사용하여 핸드셰이크 없이 첫 패킷부터 암호화된 데이터를 전송할 수 있습니다.
  • Source Address Verification: UDP의 취약점인 반사 공격(Reflection Attack)을 막기 위해, 서버는 클라이언트의 IP를 검증하는 Retry Packet 메커니즘을 사용합니다.

3.3 정교한 흐름 제어 (Flow Control)

QUIC은 TCP의 흐름 제어를 계승하면서도 멀티플렉싱 환경에 맞게 개선했습니다.

  • Stream-level vs Connection-level: 연결 전체의 버퍼 가용량뿐만 아니라, 각 스트림별로 독립적인 흐름 제어를 수행합니다. 특정 스트림이 데이터를 너무 많이 보내 연결 전체 대역폭을 독점하는 것을 방지합니다.
  • No HOL Blocking: TCP는 패킷 하나가 손실되면 윈도우 전체가 멈추지만, QUIC은 손실된 패킷이 속한 스트림만 멈추고 다른 스트림은 계속 전송됩니다.

3.4 연결 마이그레이션 (Connection Migration)

TCP 연결은 (출발지 IP, 출발지 Port, 목적지 IP, 목적지 Port)의 4-tuple로 식별됩니다. 따라서 사용자가 와이파이에서 LTE로 전환하여 IP가 바뀌면 연결이 끊어집니다.

  • Connection ID (CID): QUIC은 IP 주소가 아닌 Connection ID라는 고유 식별자를 사용하여 연결을 유지합니다.
  • 동작: 클라이언트의 IP가 바뀌어도 패킷 헤더의 Destination Connection ID가 동일하다면, 서버는 이를 기존 연결의 연속으로 인식합니다. 이를 통해 네트워크 전환 시에도 끊김 없는 통신이 가능합니다.

3.5 HTTP/3에서의 WebSocket (RFC 9220)

결론: HTTP/2와 의미론적으로 동일하지만 QUIC 스트림 위에서 동작하여 HOL Blocking이 없습니다

HTTP/3의 웹소켓도 HTTP/2와 유사하게 Extended CONNECT 방식을 사용합니다. RFC 9220 - Bootstrapping WebSockets with HTTP/3 (opens in a new tab)

RFC 9220에서 "The mechanism for running the WebSocket Protocol over a single stream of an HTTP/2 connection is equally applicable to HTTP/3" 라고 명시되어 있습니다.

HTTP/2와의 주요 차이점

구분HTTP/2 WebSocketHTTP/3 WebSocket
전송 계층TCP 스트림QUIC 스트림
설정 값SETTINGS_ENABLE_CONNECT_PROTOCOL (0x08)동일 (0x08, 별도 레지스트리)
의사 헤더:protocol = websocket동일 (:protocol = websocket)
스트림 종료TCP FINQUIC RST (H3_REQUEST_CANCELLED)
HOL Blocking발생 (TCP 계층)없음 (스트림 독립)

RFC 9220에서 "HTTP/3 settings be registered separately for HTTP/3" 라고 명시되어 있으며, 설정 값은 동일하지만 별도 레지스트리에 등록됩니다.

HTTP/3 WebSocket 요청 예시

// HTTP/3 WebSocket Request (QUIC Stream ID: 5)
HEADERS
  :method = CONNECT
  :protocol = websocket
  :scheme = https
  :path = /chat
  :authority = server.example.com
  sec-websocket-protocol = chat
  sec-websocket-version = 13
  origin = https://example.com

요청 형식은 HTTP/2와 거의 동일하지만, 내부적으로는 QUIC 스트림 위에서 동작합니다.

QUIC의 핵심 장점: 독립적 스트림 처리

핵심 차이:

  • HTTP/2: WebSocket Stream 5의 패킷 손실 시, Stream 3과 7도 TCP 계층에서 블로킹됩니다
  • HTTP/3: WebSocket Stream 5의 패킷 손실 시, Stream 3과 7은 영향 없이 계속 동작합니다

2025년 구현 현황

2025년 9월 기준, HTTP/3 WebSocket (RFC 9220)은 프로덕션 환경에서 거의 사용되지 않습니다. WebSocket.org - Future of WebSockets (opens in a new tab)

WebSocket.org에서 "As of September 2025, WebSocket over HTTP/3 (RFC 9220) has no production implementations in browsers or most web servers" 라고 명시되어 있습니다.

  • 브라우저: Chrome은 "Intent to Prototype" 단계
  • 서버: 대부분의 웹 서버에 구현되지 않음
  • 현실: 실험/개발 단계이며, 프로덕션 배포는 불가능

3.6 QUIC의 신뢰성 메커니즘 (Packet Number vs Stream Offset)

"순서를 보장하지 않는데 어떻게 손실을 감지하고 신뢰성을 확보하는가?"는 QUIC의 가장 핵심적인 질문입니다. QUIC은 **전송 순서(Transport Order)**와 **데이터 순서(Data Order)**를 분리함으로써 이 문제를 해결합니다.

1) Packet Number (전송 계층 - 신뢰성 담당)

  • 역할: 패킷의 고유 식별자이자 전송 순서를 나타냅니다.
  • 특징: 모든 패킷은 단조 증가(Monotonically Increasing)하는 고유한 번호를 가집니다. 재전송되는 패킷도 이전 번호를 쓰지 않고 새로운 번호를 부여받습니다. Wikipedia - QUIC (opens in a new tab)
  • 손실 감지: 수신측은 Packet 1, 3을 받았는데 2가 없다면, 2번 패킷의 손실을 즉시 인지하고 ACK에서 이를 알립니다. (TCP의 모호성 문제 해결)

Wikipedia QUIC 문서에서 "QUIC uses unique and monotonically increasing packet numbers compared to the wrapping TCP sequence number" 라고 명시되어 있습니다.

2) Stream ID + Offset (애플리케이션 계층 - 데이터 조립 담당)

  • 역할: 실제 데이터의 순서를 맞추는 역할을 합니다.
  • 동작: 패킷 순서가 뒤바뀌어 도착하더라도, 수신측은 각 스트림 프레임의 Offset 값을 보고 데이터를 올바른 위치에 끼워 맞춥니다.

3) 동작 예시: TCP vs QUIC 비교

시나리오: Stream A, B, C를 동시에 전송하는 상황에서 Stream B의 패킷이 손실됨

상세 동작 과정:

  • QUIC Sender:

    1. Stream A의 데이터 [0-100] 바이트를 Packet 10에 담아 보냅니다.
      • Packet Number: 10
      • Stream Frame: {Stream ID: A, Offset: 0, Length: 100, Data: ...}
    2. Stream B의 데이터를 Packet 11에 담아 보내지만 손실됩니다.
    3. Stream C의 데이터를 Packet 12에 담아 보냅니다.
    4. ACK를 통해 Packet 11의 손실을 감지합니다.
    5. 동일한 Stream B 데이터를 Packet 15 (새 번호)에 담아 재전송합니다.
      • Packet Number: 15 (11을 재사용하지 않음)
      • Stream Frame: {Stream ID: B, Offset: 0, Length: 100, Data: ...} (동일한 offset)
  • QUIC Receiver:

    1. Packet 10, 12가 도착하면:
      • Stream A의 Offset 0에 데이터 저장 → 즉시 처리 완료
      • Stream C의 Offset 0에 데이터 저장 → 즉시 처리 완료
    2. Packet 11이 없지만 다른 스트림은 영향 없음 (HOL Blocking 없음)
    3. Packet 15가 도착하면:
      • Packet Number는 15이지만, Stream B의 Offset 0을 보고 올바른 위치에 저장
      • Stream B도 처리 완료
    4. 결과: 전송 순서는 바뀌었지만(10, 12, 15), 모든 스트림의 데이터는 완벽하게 복구

핵심 차이점:

  • TCP: Packet 11 손실 시 Packet 12도 블로킹 (순서 보장 때문에)
  • QUIC: Packet 11 손실해도 Packet 12는 즉시 처리 (스트림별 독립적 관리)

4. 요약 비교

특징HTTP/1.1HTTP/2HTTP/3 (QUIC)
기반 프로토콜TCPTCPUDP (QUIC)
데이터 전송 단위텍스트 (줄바꿈)바이너리 프레임바이너리 프레임 (QUIC 패킷)
멀티플렉싱불가 (순차 처리)가능 (Stream)가능 (Independent Stream)
HOL Blocking발생 (App & TCP)발생 (TCP Only)해결 (없음)
헤더 압축없음HPACKQPACK
WebSocket연결 전체 점유 (Upgrade)단일 스트림 점유 (CONNECT)단일 스트림 점유 (CONNECT)
연결 식별IP + PortIP + PortConnection ID

5. 시각적 비교


부록 A. 프로토콜의 핵심 개념

이 부록에서는 HTTP 프로토콜의 진화를 이해하는 데 필요한 핵심 개념들을 상세히 다룹니다.

A.1 텍스트 기반 vs 바이너리 기반 프로토콜

결론: HTTP/2는 텍스트에서 바이너리로 전환하여 성능을 대폭 향상시켰습니다

HTTP/1.1은 사람이 읽을 수 있는 텍스트 형식이지만, HTTP/2와 HTTP/3는 바이너리 형식으로 기계의 효율적인 처리에 최적화되었습니다. DigitalOcean - HTTP/1.1 vs HTTP/2 (opens in a new tab)

DigitalOcean에서 "Binary protocols consume less bandwidth, are more efficiently parsed and are less error-prone than the textual protocols used by HTTP/1.1" 이라고 명시되어 있습니다.

HTTP/1.1 텍스트 기반 프로토콜

구조 예시:

GET /index.html HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml
Accept-Language: en-US,en;q=0.9

특징:

  • 사람이 읽을 수 있음: curl, telnet으로 직접 요청 작성 가능
  • 공백과 줄바꿈 처리: \r\n으로 줄 구분, 공백 처리 규칙이 복잡
  • 파싱 오버헤드: 문자열 파싱, 토큰화, 유효성 검사 등의 추가 비용
  • 디버깅 용이: 네트워크 패킷을 그대로 읽어서 문제 파악 가능

Cloudflare - HTTP/2 vs HTTP/1.1 (opens in a new tab)

Cloudflare에서 "HTTP/1.1 messages are human-readable, while HTTP/2 uses a binary format. This makes HTTP/2 easier for machines to parse but difficult for humans to read" 라고 명시되어 있습니다.

HTTP/2, HTTP/3 바이너리 프로토콜

구조 (HTTP/2 프레임 예시):

+-----------------------------------------------+
|                 Length (24)                   |
+---------------+---------------+---------------+
|   Type (8)    |   Flags (8)   |
+-+-------------+-------------------------------+
|R|                 Stream ID (31)              |
+=+==============================================+
|                   Frame Payload              |
+-----------------------------------------------+

장점: High Performance Browser Networking (opens in a new tab)

O'Reilly의 "High Performance Browser Networking"에서 "The binary framing layer is responsible for all performance enhancements and is the foundation that enables all other function and performance optimizations provided by HTTP/2" 라고 명시되어 있습니다.

  1. 효율적인 파싱:

    • 바이트 오프셋으로 프레임 경계를 즉시 파악
    • 문자열 처리 없이 메모리에서 직접 읽기
  2. 낮은 오버헤드:

    • 압축 효율성 향상 (HPACK, QPACK)
    • 불필요한 공백, 줄바꿈 제거
  3. 프레임 경계 명확: http2-explained (opens in a new tab)

    http2-explained에서 "HTTP/2 is binary to make the framing much easier, as figuring out the start and the end of frames is one of the really complicated things in HTTP 1.1" 이라고 명시되어 있습니다.

  4. 보안 강화:

    • HTTP Response Splitting 공격 방지
    • 명확한 프레임 구조로 인한 파싱 오류 감소

비교표

특성텍스트 기반 (HTTP/1.1)바이너리 기반 (HTTP/2, HTTP/3)
가독성✅ 사람이 읽을 수 있음❌ 바이너리로 인코딩
파싱 속도느림 (문자열 처리 필요)빠름 (바이트 오프셋)
압축 효율낮음높음 (HPACK, QPACK)
오버헤드높음 (공백, 줄바꿈)낮음 (고정 길이 필드)
디버깅쉬움 (텍스트 확인)어려움 (도구 필요)
프레임 경계복잡 (\r\n 파싱)명확 (Length 필드)

RFC 9113 - HTTP/2 (opens in a new tab)

RFC 9113에서 "HTTP/2 also enables more efficient processing of messages through use of binary message framing" 라고 명시되어 있습니다.


A.2 스트림(Stream)의 개념

결론: 스트림은 데이터의 논리적 흐름을 나타내는 추상화이며, HTTP/2와 QUIC에서 멀티플렉싱의 기본 단위입니다

스트림은 "순서 보장 바이트 시퀀스"를 의미하는 추상적 개념으로, 각 HTTP 버전에서 다르게 구현됩니다. High Performance Browser Networking - HTTP/2 (opens in a new tab)

O'Reilly 책에서 "The ability to break down an HTTP message into independent frames, interleave them, and then reassemble them on the other end is the single most important enhancement of HTTP/2" 라고 명시되어 있습니다.

TCP의 단일 바이트 스트림

특징:

  • 전체 연결 = 하나의 스트림: 모든 데이터가 단일 순서 보장 바이트 스트림으로 처리됩니다
  • 전역 순서 보장: 바이트 1, 2, 3, ..., N이 순서대로 전달되어야 합니다
  • HOL Blocking 발생: 한 바이트라도 손실되면 전체 스트림이 블로킹됩니다
TCP 연결
┌─────────────────────────────────────┐
│ 단일 바이트 스트림                     │
│ [byte 1][byte 2][byte 3]...[byte N]  │
│ 전체 순서 보장 필수                    │
└─────────────────────────────────────┘

APNIC Blog - Comparing TCP and QUIC (opens in a new tab)

APNIC Blog에서 "TCP provides an interface for sending streams of data between two endpoints, ensuring data reaches the other end in the exact same form. However, TCP will see any error on a connection as a blocking operation" 이라고 명시되어 있습니다.

HTTP/2의 논리적 스트림 (TCP 위)

특징:

  • 가상 채널: TCP 연결 위에 논리적으로 여러 스트림을 생성합니다
  • 스트림 ID로 구분: 각 프레임은 고유한 Stream ID를 가집니다
  • 멀티플렉싱: 여러 스트림의 프레임이 인터리빙됩니다
  • TCP HOL Blocking 여전히 존재: TCP 레벨에서는 여전히 순서 보장이 필요합니다
HTTP/2 (TCP 위)
┌─────────────────────────────────────┐
│ TCP: 단일 바이트 스트림                │
│ [S1][S2][S1][S3][S2][S1][S3]...     │
└─────────────────────────────────────┘
         ↓ HTTP/2가 Stream ID로 재조립
┌─────────┬─────────┬─────────┐
│Stream 1 │Stream 2 │Stream 3 │
│ [D1][D2]│ [D1][D2]│ [D1][D2]│
└─────────┴─────────┴─────────┘

RFC 9113 - HTTP/2 (opens in a new tab)

RFC 9113에서 "A stream is an independent, bidirectional sequence of frames exchanged between the client and server within an HTTP/2 connection" 이라고 명시되어 있습니다.

QUIC의 독립적 스트림

특징:

  • 스트림 간 순서 보장 안 함: 각 스트림 내에서만 순서를 유지합니다
  • 독립적 흐름 제어: 각 스트림이 자체 흐름 제어를 가집니다
  • 독립적 손실 복구: 한 스트림의 패킷 손실이 다른 스트림에 영향을 주지 않습니다
  • No HOL Blocking: 진정한 의미의 멀티플렉싱을 구현합니다
QUIC
┌─────────────────────────────────────┐
│ UDP 패킷 (순서 보장 안 함)             │
│ [P1:S1][P2:S2][P3:S1][P4:S3]...     │
└─────────────────────────────────────┘
         ↓ QUIC이 Stream ID + Offset으로 재조립
┌─────────┬─────────┬─────────┐
│Stream 1 │Stream 2 │Stream 3 │  ← 독립적!
│ [D1][D2]│ [D1][D2]│ [D1][D2]│
└─────────┴─────────┴─────────┘

RFC 9000 - QUIC Transport (opens in a new tab)

RFC 9000에서 "QUIC does not guarantee ordering of bytes between different streams" 이라고 명시되어 있습니다.

스트림 개념 비교

특성TCPHTTP/2QUIC
스트림 개수1개 (전체 연결)다수 (논리적)다수 (네이티브)
순서 보장전체 바이트 순서스트림 내 + TCP 전체스트림 내만
흐름 제어연결 레벨연결 + 스트림 레벨연결 + 스트림 레벨
독립성없음부분적 (TCP 의존)완전 독립
HOL Blocking발생발생 (TCP 레벨)없음

A.3 TCP Stream vs QUIC Stream: 근본적 차이

결론: TCP는 전체 연결이 하나의 순서 보장 스트림이지만, QUIC은 여러 독립 스트림을 하나의 연결에서 처리합니다

이 차이가 HTTP/2의 TCP HOL Blocking과 HTTP/3의 No HOL Blocking을 결정합니다. Wikipedia - QUIC (opens in a new tab)

Wikipedia에서 "QUIC establishes a number of multiplexed connections between two endpoints using User Datagram Protocol (UDP). Critically, all streams are independent, they manage their flow control and their lost data retransmission" 이라고 명시되어 있습니다.

TCP Stream의 구조

단일 바이트 스트림 모델:

TCP는 전체 연결을 하나의 순서 보장 바이트 스트림으로 취급합니다.

클라이언트 → 서버
─────────────────────────────────────────
TCP 연결 (단일 스트림)

전송: [파일A: 0-99][파일B: 0-99][파일C: 0-99]
       ↓ TCP Sequence Number로 관리
      [Seq 1-100][Seq 101-200][Seq 201-300]

만약 Seq 101-200 (파일B) 손실:
- Seq 201-300 (파일C)도 도착했지만
- TCP는 애플리케이션에 전달하지 않음
- 이유: 바이트 스트림의 순서를 보장해야 하므로

문제점:

  • 파일 B의 데이터가 손실되면 파일 C도 블로킹됨
  • TCP는 "파일" 개념을 모르고 오직 "바이트 순서"만 관리

Chromium - QUIC (opens in a new tab)

Chromium에서 "TCP will see any error on a connection as a blocking operation, stopping further transfers until the error is resolved" 라고 명시되어 있습니다.

QUIC Stream의 구조

다중 독립 스트림 모델:

QUIC은 여러 독립적인 스트림을 하나의 연결에서 처리하며, 각 스트림은 자체적으로 순서를 관리합니다.

클라이언트 → 서버
─────────────────────────────────────────
QUIC 연결 (다중 독립 스트림)

Stream 1 (파일A): Offset 0-99
Stream 2 (파일B): Offset 0-99
Stream 3 (파일C): Offset 0-99

만약 Stream 2의 패킷 손실:
- Stream 1, 3은 정상 처리 ✓
- Stream 2만 재전송 대기
- 이유: 스트림 간 순서 의존성이 없으므로

장점:

  • 스트림별 독립적 흐름 제어
  • 스트림별 독립적 손실 복구
  • 진정한 멀티플렉싱 구현

RFC 9000 - QUIC Transport (opens in a new tab)

RFC 9000에서 "QUIC connections are multiplexed: each stream runs independently with separate flow control and delivery state. Packet loss on one stream doesn't interfere with others" 라고 명시되어 있습니다.

흐름 제어(Flow Control)의 차이

TCP 흐름 제어:

TCP 연결
┌────────────────────────────┐
│ 윈도우 크기: 64KB          │
│ 모든 데이터 공유            │
└────────────────────────────┘

문제: 윈도우가 0이 되면 모든 데이터 전송 중단

QUIC 흐름 제어:

QUIC 연결
┌────────────────────────────┐
│ 연결 레벨 윈도우: 1MB       │
│   └─ Stream 1: 256KB       │
│   └─ Stream 2: 256KB       │
│   └─ Stream 3: 256KB       │
└────────────────────────────┘

장점: 각 스트림이 독립적으로 흐름 제어

Michelin Blog - QUIC Analysis (opens in a new tab)

Michelin Blog에서 "While it's possible to multiplex a TCP session into streams, all such multiplexed TCP streams share a single flow control state. If the TCP receiver advertises a zero-sized window, all multiplexed streams are blocked" 이라고 명시되어 있습니다.

패킷 손실 시 동작 비교

시나리오: 3개 파일 전송 중 파일 B의 패킷 손실

TCP (HTTP/2):

1. 전송: [A1][B1][C1][A2][B2][C2]
2. B1 패킷 손실
3. TCP: C1, A2, B2, C2가 도착해도 모두 버퍼에 보관
4. 이유: B1 이후 모든 바이트는 순서 보장을 위해 블로킹
5. 결과: 전체 지연 발생

QUIC (HTTP/3):

1. 전송: [A1][B1][C1][A2][B2][C2]
2. B1 패킷 손실
3. QUIC: C1, A2, C2는 즉시 애플리케이션에 전달
4. 이유: 스트림 간 순서 의존성 없음
5. 결과: A와 C는 정상 처리, B만 대기

최종 비교표

특성TCP StreamQUIC Stream
스트림 개수1개 (연결 = 스트림)다수 (독립적)
순서 보장 범위전체 바이트스트림 내만
흐름 제어연결 레벨만연결 + 스트림 레벨
패킷 손실 영향전체 연결 블로킹해당 스트림만
HOL Blocking발생없음
멀티플렉싱HTTP/2가 논리적으로 구현네이티브 지원
성능패킷 손실 시 전체 지연패킷 손실 시 부분 지연

6. 참고 자료 (References)

  • 이해-췍

WebSocket Protocol:

TCP HOL Blocking:

QUIC Protocol:

WebSocket over HTTP/2 and HTTP/3:

Text vs Binary Protocols:

Stream Concepts and Multiplexing:

HTTP Version Comparison: