Post

HTTP

HTTP

HTTP는 처음 등장한 이후 지금까지 “더 빠르게, 더 효율적으로” 통신하기 위한 방향으로 계속 발전해왔다. 각 버전은 이전 버전에서 드러난 성능 한계를 해결하기 위해 등장했고, 그 한계를 이해하면 다음 버전이 왜 그런 구조를 택했는지 자연스럽게 이해할 수 있다.

HTTP/1

HTTP/1 이미지

  • 정의: 1996년에 표준화된 초기 HTTP(HTTP/1.0)로, 클라이언트가 요청을 보내고 서버가 응답하는 가장 기본적인 요청-응답(Request-Response) 구조를 확립했다.
  • 동작 원리
    • 통신 내용이 사람이 읽을 수 있는 텍스트(Plain Text) 형태로 오간다 (GET /index.html HTTP/1.0 같은 요청 라인 + 헤더 + 바디).
    • 요청 하나를 보낼 때마다 새로운 TCP 연결을 맺고, 응답을 받으면 그 연결을 바로 끊는다(Non-persistent Connection). 즉 요청과 TCP 연결이 1:1로 대응한다.
    • 상태 코드(200, 404 등), Content-Type 같은 헤더가 도입되어 HTML뿐 아니라 이미지, 파일 등 다양한 형식을 주고받을 수 있게 되었다.
  • 한계
    • 웹페이지 하나를 렌더링하려면 HTML, CSS, JS, 이미지 등 리소스를 각각 별도로 요청해야 하는데, 리소스마다 TCP 3-way handshake(그리고 HTTPS라면 TLS handshake까지)를 매번 새로 거쳐야 해서 오버헤드가 매우 컸다.
    • 페이지에 리소스 수가 늘어날수록 “연결 수립 → 요청 → 응답 → 연결 종료”를 반복하는 비용이 누적되어 체감 속도가 크게 느려졌다.

HTTP/1.1

HTTP/1.1 이미지

1997년에 표준화되며 HTTP/1.0의 “매 요청마다 새 연결”이라는 구조적 비효율을 개선하는 데 초점을 맞췄다.

  • Persistent Connection (Keep-Alive)
    • 하나의 TCP 연결을 끊지 않고 유지하면서 여러 번의 요청/응답을 순차적으로 주고받을 수 있게 되었다(기본값으로 활성화).
    • 매 요청마다 TCP handshake를 반복하지 않아도 되므로 연결 수립 오버헤드가 크게 줄었다.
  • Pipelining
    • 이전 응답을 기다리지 않고 다음 요청을 연달아 보낼 수 있는 기능도 추가되었지만, 서버는 응답을 반드시 요청이 온 순서대로 돌려줘야 한다는 제약이 있어 실효성이 크지 않았고, 대부분의 브라우저가 기본적으로 비활성화했다.
  • 그 외 개선
    • Host 헤더가 필수화되어 하나의 IP 주소로 여러 도메인을 운영하는 가상 호스팅(Virtual Hosting)이 가능해졌다.
    • Chunked Transfer Encoding이 도입되어 응답 크기(Content-Length)를 미리 알 수 없는 상황에서도 데이터를 조각(chunk) 단위로 스트리밍할 수 있게 되었다.
    • Cache-Control 등 캐시 관련 헤더가 강화되어 불필요한 재요청을 줄일 수 있게 되었다.
  • 여전히 남은 한계: HOL(Head-of-Line) Blocking
    • 하나의 연결에서는 응답이 순서대로 와야 하기 때문에, 앞선 요청의 응답이 늦어지면 뒤에 대기 중인 요청들도 함께 지연된다.
    • 브라우저들은 이를 우회하기 위해 도메인당 6개 정도의 TCP 연결을 동시에 맺는 방식으로 병렬성을 확보했지만, 이는 연결 수 자체가 늘어나 서버/네트워크 자원을 더 많이 소모하고, 여전히 각 연결 안에서는 순서를 기다려야 하는 근본적인 해결책은 아니었다.

HTTP/2

HTTP/2 이미지

2015년 Google의 SPDY 프로토콜을 기반으로 표준화되었으며, HTTP/1.1의 HOL Blocking 문제를 HTTP 계층에서 해결하는 데 집중했다.

  • 바이너리 프레이밍(Binary Framing Layer): 텍스트 기반 메시지 대신 바이너리 형식의 프레임 단위로 통신한다. 파싱이 더 빠르고 정확하며 파싱 오류의 여지가 줄어든다.
  • 멀티플렉싱(Multiplexing): 하나의 TCP 연결 위에서 여러 개의 스트림(요청/응답)을 동시에 주고받을 수 있다. 각 스트림의 프레임이 서로 잘게 쪼개져 인터리빙(interleaving)되어 전송되므로, 앞선 요청 하나가 느리다고 해서 뒤의 다른 요청까지 밀리지 않는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
HTTP/1.1 (up to ~6 parallel TCP connections per domain)

conn A:  [--- req 1 ---][--- req 2 ---]
conn B:  [--- req 3 ---][--- req 4 ---]
conn C:  [--- req 5 ---]

  -> requests on the SAME connection are still processed strictly in order
     (a slow request 1 blocks request 2 right behind it)

HTTP/2 (1 TCP connection, multiple multiplexed streams)

stream 1: [-req-][-------resp-------]
stream 2:      [-req-][--resp--]
stream 3:           [-req-][----resp----]
connection: |--------------------------------------------|
             frames from all 3 streams are interleaved on the same connection

  -> a slow stream no longer blocks the other streams at the HTTP layer
  • 헤더 압축(HPACK): 요청마다 반복되는 헤더(쿠키, User-Agent 등)를 압축 테이블로 관리해 중복 전송을 줄인다.
  • 스트림 우선순위(Stream Prioritization): 클라이언트가 스트림 간 우선순위와 의존 관계를 지정해, 중요한 리소스(예: CSS)를 먼저 받도록 서버에 힌트를 줄 수 있다.
  • 서버 푸시(Server Push): 클라이언트가 요청하기 전에 서버가 필요할 것으로 예상되는 리소스를 미리 보내주는 기능도 도입되었으나, 실제로는 캐시 활용도가 낮고 오히려 불필요한 데이터를 보내는 경우가 많아 이후 대부분의 브라우저에서 지원이 중단되었다.
  • 여전히 남은 한계: TCP 레벨 HOL Blocking
    • HTTP 계층에서는 스트림을 나눴지만, 결국 이 스트림들은 하나의 TCP 연결을 공유한다.
    • TCP는 패킷을 순서대로 전달해야 하는 프로토콜이라, 그중 패킷 하나라도 유실되면 TCP가 재전송을 마칠 때까지 같은 연결을 쓰는 모든 스트림이 함께 멈춰버린다. HTTP 계층의 멀티플렉싱만으로는 해결할 수 없는, 전송 계층(TCP) 자체의 구조적 한계다.

HTTP/3 on QUIC

HTTP/3 이미지

HTTP/2가 해결하지 못한 TCP 레벨의 HOL Blocking을 근본적으로 없애기 위해, 아예 전송 계층을 TCP에서 UDP 기반의 QUIC으로 교체한 버전이다. 2022년 정식 표준(RFC 9114)으로 제정되었다.

  • 독립적인 스트림: QUIC은 스트림마다 유실/재전송을 독립적으로 관리한다. 한 스트림에서 패킷이 유실되어도 그 스트림만 재전송을 기다릴 뿐, 다른 스트림은 영향을 받지 않는다 → TCP 레벨 HOL Blocking이 완전히 사라진다.
  • 연결과 TLS의 통합, 빠른 handshake: 기존에는 TCP handshake와 TLS handshake가 별도로 이루어져 왕복(RTT)이 여러 번 필요했지만, QUIC은 전송 계층 연결 수립과 TLS 1.3 handshake를 하나로 합쳐 1-RTT(신규 연결) 또는 0-RTT(이전에 접속한 적 있는 서버에 재연결)로 통신을 시작할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
TCP + TLS 1.2 (typical HTTPS handshake, ~2-3 RTT before any data)

Client                                    Server
  |------ TCP SYN ------------------------->|
  |<----- TCP SYN-ACK -----------------------|   RTT 1 (TCP handshake)
  |------ TCP ACK --------------------------->|
  |------ TLS ClientHello ------------------->|
  |<----- TLS ServerHello, Cert, ... ---------|   RTT 2 (TLS handshake)
  |------ TLS Finished ----------------------->|
  |------ HTTP request ------------------------>|   RTT 3 (first real data)

QUIC (transport + TLS 1.3 combined, 1-RTT / 0-RTT)

Client                                    Server
  |------ QUIC Initial (+ TLS ClientHello) ->|
  |<----- QUIC Initial (+ TLS ServerHello) ---|   RTT 1 (transport + TLS combined)
  |------ HTTP request ------------------------>|   data can follow right after RTT 1
                                                   (0-RTT possible on reconnect with cached session)
  • Connection Migration(연결 마이그레이션): TCP는 (출발지 IP, 출발지 Port, 목적지 IP, 목적지 Port) 조합으로 연결을 식별하기 때문에 와이파이에서 LTE로 네트워크가 바뀌면 연결이 끊긴다. QUIC은 IP/Port 대신 자체적인 Connection ID로 연결을 식별하므로, 네트워크가 바뀌어도 같은 연결을 그대로 이어갈 수 있다 → 모바일 환경에서 특히 유리하다.
  • 기본 암호화: QUIC은 TLS 1.3을 프로토콜 자체에 내장하고 있어 평문 통신을 지원하지 않는다. 즉 QUIC 위에서는 항상 암호화된 통신이 이루어진다.
  • QPACK: HTTP/2의 HPACK과 유사한 헤더 압축 기법이지만, 스트림 간 독립성을 해치지 않도록 재설계된 압축 방식을 사용한다.

정리

구분HTTP/1.0HTTP/1.1HTTP/2HTTP/3 (QUIC)
전송 계층TCPTCPTCPUDP 기반 QUIC
연결 방식요청마다 새 연결Persistent Connection단일 연결 + 멀티플렉싱단일 연결(Connection ID) + 멀티플렉싱
데이터 형식텍스트텍스트바이너리 프레임바이너리 프레임
헤더 압축없음없음HPACKQPACK
HOL Blocking요청이 직렬 처리되어 사실상 없음(대신 매우 느림)있음 (연결 내)TCP 레벨에서 있음없음 (스트림이 서로 독립적)
HandshakeTCP만TCP만TCP + TLS 별도통합 1-RTT/0-RTT
네트워크 전환 시연결 끊김연결 끊김연결 끊김Connection Migration으로 유지

결국 HTTP의 발전 과정은 “연결을 얼마나 적게, 얼마나 효율적으로 맺고 유지할 것인가”와 “여러 요청을 어떻게 서로 방해하지 않고 동시에 처리할 것인가”라는 두 가지 문제를 계속 더 깊은 계층(애플리케이션 → TCP 위의 멀티플렉싱 → 전송 계층 자체 교체)에서 풀어온 과정이라고 볼 수 있다.

This post is licensed under CC BY 4.0 by the author.

Trending Tags