[네트워크] HTTP 프로토콜 파헤치기 - 버전 별 특징
들어가며...
HTTP는 웹을 배우는 개발자라면 필수적으로 알아야하고 활용해야 하는 프로토콜이다.
하지만, HTTP 프로토콜의 진화 과정에 대해서 질문을 한다면 의외로 답변하기 힘들어하는 경우가 많다.
해당 포스팅 글 에서는 HTTP 프로토콜의 진화 과정에 대해서 버전 별로 어떤 문제점이 있고 다음 버전에서는 어떻게 개선되었는지, 그리고 요청과 응답에 대한 예시 메시지를 보면서 최대한 자세히 내용을 다뤄보려고 한다.
HTTP/0.9
HTTP 초기 버전에는 버전 번호가 없었다. HTTP/0.9는 이후 버전과 구별하기 위해 0.9로 불리게 되었다.
요청
GET /index.html
HTTP/0.9는 One-Line-Protocol로 불리는데, 말 그대로 요청이 단일 라인으로 구성되어 있어 놀라울 정도로 단순하다.
HTTP/0.9의 요청 메서드는 GET 메서드가 유일하다고 한다. 즉, 리소스를 가져오는 요청만 할 수 있다.
이미 TCP/IP Handshake를 통해 요청 대상과 IP 주소 및 포트 번호를 교환하며 연결을 맺기 때문에
재밌게도 요청을 보낼 때 프로토콜, 서버 및 포트가 필요하지 않으므로 전체 URL은 포함하지 않았다.
응답
<html> Hello World? </html>
응답 또한 극도로 단순하다. 오로지 파일 내용 자체로만 구성된다.
헤더가 없어서 Content-Type을 명시되지 않기 때문에 해당 파일 타입은 HTML 파일만 전송될 수 있었다.
문제점
- 요청 메시지에는 헤더 정보가 없어서 파일 타입, 포맷 형태, 압축 방식 등 추가 정보를 전달할 수 없다.
- 응답 메시지에 상태 정보를 담지 않기 때문에 에러를 처리하기 위해서 번거로운 작업을 필요로 한다.
- 오직 GET 메서드만 지원하기 때문에 다양한 요청을 할 수가 없다.
HTTP/1.0
HTTP는 매우 제한적이었으며, 확장성 또한 좋지 않았기 때문에, 이를 보완하기 위해서 HTTP/1.0이 출시되었다.
요청
GET /mypage.html HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)
POST /form.php HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
name=John+Doe&email=john@xyz.com
가장 먼저 보이는 특징으로는 Header 정보와 HTTP Version이 메시지에 포함되었다는 것이다.
그리고 1.0은 GET 요청 뿐만 아니라 POST, HEAD 메서드가 추가 됨에 따라 클라이언트는 리소스를 요청하는 것 뿐만 아니라 데이터를 보낼 수도 있게 되었다.
응답
200 OK
Date: Tue, 15 Nov 1994 08:12:31 GMT
Server: CERN/3.0 libwww/2.17
Content-Type: text/html
<HTML> A page with an image <IMG SRC="/myimage.gif"> </HTML>
200 OK
Date: Tue, 15 Nov 1994 08:12:32 GMT
Server: CERN/3.0 libwww/2.17
Content-Type: text/gif
(image content)
응답 메시지도 1.0으로 넘어오면서 Status Code가 추가되었다.
이제 클라이언트는 응답 메시지의 Status Code에 따라서 에러를 처리할 수 있으므로 0.9와 비교해서 간단해졌다.
HTML 파일만 지원했던 0.9와 다르게 1.0은 Content-Type 헤더로 인해서 이제 다양한 데이터 타입을 보내줄 수 있게 되었다.
문제점

- 0.9, 1.0 버전 모두 하나의 데이터를 받기 위해서 계속해서 TCP 3-way handshake와 4-way handshake를 통해 연결을 맺고 끊는 작업을 매번 실행해야 하기 때문에 속도가 매우 느리고 서버 부하 비용이 증가한다.
- 헤더 필드에 대한 규정이 모호했기 때문에 특정 사이트들 간의 비호환성 문제가 발생했다
HTTP/1.1
HTTP 1.1 버전에서는 HTTP의 첫 번째 표준화 버전으로,
1.0 버전에서의 Connection 한 개당 하나의 요청을 처리하도록 설계된 점을 개선하기 위해서 다양한 방법들이 도입된다.
Persistent Connection

지정한 tiimeout 동안 커넥션을 닫지 않는 방식으로, 특정 시간 동안 여러 요청이 올 경우 Connection을 유지한다.
이렇게 되면, 불필요하게 요청 마다 Connection을 맺는 작업을 없앨 수 있다.
Pipeline

Persistent Connection에서 추가적으로 도입된 Pipeline 방식은 기존에는 요청을 보내고 응답을 받고 다음 요청을 보내는 순차적인 방식이 아니라 하나의 Connection에서 응답을 기다리지 않고 순차적으로 여러 요청을 연속적으로 보내 그 순서에 맞춰서 응답을 받는 형식으로, 각각 요청을 매번 보내지 않고 한꺼번에 보냄으로써 효율적으로 데이터를 주고 받을 수 있게 개선되었다.
요청
GET /en-US/docs/Glossary/Simple_header HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/en-US/docs/Glossary/Simple_header
HTTP 1.0 버전은 공식적으로는 Host 헤더를 요청하지 않는데 반해 HTTP 1.1 버전은 프록시 서버를 이용하는 서버처럼 라우팅 해주기 위해서 사양에 따라 이를 요구한다. 그리고 PUT, PATCH, DELETE, CONNECT, TRACE, OPTIONS 메서드가 추가되었다.
다양한 메서드가 추가됨에 따라서 RESTful 아키텍처 스타일의 웹 서비스와 API들이 많이 등장하게 되었다.
응답
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Wed, 20 Jul 2016 10:55:30 GMT
Etag: "547fa7e369ef56031dd3bff2ace9fc0832eb251a"
Keep-Alive: timeout=5, max=1000
Last-Modified: Tue, 19 Jul 2016 00:59:33 GMT
Server: Apache
Transfer-Encoding: chunked
Vary: Cookie, Accept-Encoding
(Content)
요청과 응답 모두 언어, 인코딩 혹은 타입을 포함한 컨텐츠 협상이 도입되어, 클라이언트와 서버로 하여금 교환하려는 가장 적합한 컨텐츠를 메시지 헤더를 통해 합의를 할 수 있게 되었다.
문제점

- Head Of Line (HOL) Blocking이라고 불리는 문제점은 순서에 맞춰서 응답하는 Pipeline 방식에서 첫 번째 요청이 처리하는 시간이 너무 오래 걸려 다음 요청이 아무리 빨리 끝나는 요청이라도 첫 번째 요청이 끝날 때까지 기다려야 하는 문제점이다.
- 기존의 HTTP 1.1 버전에서 요청이 연속적으로 이루어질 때, 헤더의 값이 중복되는게 있지만 그 데이터를 중복적으로 보내게 된다는 비효율적인 측면이 있기 때문에 불필요한 데이터를 주고받게 된다는 문제점이 있다.
SPDY

Google은 전송 지연의 문제의 해결에 집중하여 HTTP를 고속화한 새로운 프로토콜인 SPDY를 구현했다.
'Make the Web Faster' 의 목표 하나로 제안한 새로운 프로토콜로, 실제로 HTTP/1.1에 비해 상당한 성능 향상 및 효율성을 보여줬고 이는 HTTP/2.0의 초안의 참고 규격이 되었다.
주요 특징으로는 다음과 같다.
항상 TLS 위에서 동작
HTTPS로 작성된 웹 사이트에서만 적용이 가능했다.
HTTP 헤더 압축
요청이 많아질 수록 압축률을 높여서 대역폭이 작은 모바일 환경에서 효과를 크게 보였다.
텍스트가 아닌 바이너리 방식
파싱이 더 빠르고, 오류 발생 가능성이 낮아졌다.
Multiplexing
하나의 커넥션 안에서 다수의 독립적인 스트림을 동시에 처리한다. (비동기 방식)
HTTP/2.0
위의 HTTP 1.1의 문제점을 보완하고 SPDY를 참고하여 나온 HTTP 2.0은 다음과 같은 특징을 가진다.
Binary Framing Layer
HTTP 2.0 성능 향상의 핵심으로, HTTP 메시지가 캡슐화되어 클라이언트와 서버 간에 전송되는 방식이다.
계층 구조를 잠깐 살펴보자면, TCP 윗 계층에서 해당 Binary Framing 기법이 적용된 것을 볼 수 있는데 이는, 기존의 HTTP 메시지 방식을 그대로 가져가되, 전송 중에 인코딩 되는 방식이 다르다. 기존의 Plain Text로 되어있는 이전 버전과 다르게 2.0 버전은 Header와 Data를 frame 단위로 나눠서 바이너리 형식으로 인코딩 시킨다. 결과적으로 클라이언트와 서버는 서로를 이해하기 위해서 합의된 바이너리 인코딩 매커니즘을 사용해야 한다.
Multiplexed Streams

HTTP 2.0 버전은 하나의 TCP 연결을 통해서 여러 데이터 요청을 병렬적으로 전송할 수 있다.
HTTP 1.1 버전은 응답 순서가 중요했지만, 2.0 버전에서는 순서에 상관없이 Stream으로 주고 받을 수 있어서 첫 번째 요청이 오래걸려도 이후에 보냈던 요청들은 모두 비동기적으로 처리가 되기 때문에 RTT 시간을 줄일 수 있다.
Stream, Message, Frame

새로운 Binary Framing 매커니즘이 도입됨에 따라서 클라이언트와 서버 간에 데이터 교환 방식이 변경되었다.
- Stream: 연결된 Connection 내에서 양방향으로 Message를 주고 받는 하나의 통로
- Message: 요청 또는 응답의 단위이며, 다수의 Frame으로 이루어진 라인이다.
- Frame: HTTP/2 에서의 통신 최소 단위이며, Header 또는 Data가 들어있다.
정리함녀 Frame 단위로 이루어진 요청과 응답 Message는 하나의 Stream을 통해 이루어지며, 이러한 Stream 들은 하나의 Connection 내에서 병렬적으로 처리된다.
Header Compression

HTTP 1.1 버전에서의 문제점이었던 중복 헤더에 대해서 2.0 버전은 클라이언트와 서버는 모두 이전에 본 헤더 필드에 대한 색인 생성 목록을 유지 관리하고 업데이트하면서 헤더의 중복되는 필드를 재전송 하지 않도록 하여, 데이터를 절약한다.
또한 기존에 HTTP Header는 Plain Text 이었지만, HTTP 2.0 버전에서는 Hash Table과 정적 Huffman Coding을 사용하는 HPACK이라는 압축 방식으로 인코딩하여 개별 전송 크기를 획기적으로 줄인다.
Server Push

단일 클라이언트 요청에 대해서 여러 응답을 보낼 수 있는 기능으로, 기존에는 웹 사이트를 구성하는데 필요한 리소스를 서버에서 제공하는 문서를 통해 클라이언트가 검색하여 요청을 보내는 방식을 탈피하고 추가 지연 시간을 없애기 위해서 서버가 관련 리소스를 미리 푸시하도록 하여 성능을 높이는 기법이다.
Stream Prioritization

병렬적으로 요청/응답을 수행하면 패킷이 오는 순서는 뒤죽박죽 섞여 있을 것이다.
하지만, 웹 브라우저를 예로 들어서 해당 리소스를 구성하기 위해서는 HTML > CSS > JS 순으로 렌더링이 되어야 한다.
따라서 각각의 스트림 데이터에 대해서 우선 순위를 지정해줄 필요가 있는데 해당 기법이 바로 Stream Prioritization이다.
HTTP 2.0 에서는 클라이언트에서 우선순위 지정 트리를 사용하여 스트림에 식별자를 설정함으로써 해결하였다.
각각의 스트림은 1 ~ 256 까지의 가중치를 가지고 하나의 스트림은 다른 스트림에게 명확한 의존성을 가진다.
문제점
- 여전히 TCP 기반이기에 3-way handshake 및 TLS handshake 과정이 있으므로 RTT로 인한 지연 시간이 발생한다.
- 이 역시도 TCP 기반이기 때문에, HTTP 계층 즉 Application 계층 에서의 Head Of Line Blocking은 해소됐을지 몰라도 TCP 프로토콜에서 발생하는 TCP Head Of Line Blocking은 해소하지 못한다는 단점이 있다.

스트림 B에 대한 데이터가 중간에 유실되거나 훼손되었다면 해당 패킷에 대한 재전송 요청을 보내고 이후에 받은 패킷들은 패킷을 다시 받을 때까지 pending 상태에 들어간다. 따라서, 스트림 B 데이터와 상관이 없는 스트림 A, C 데이터도 영향을 받는다는 말이 된다.
HTTP/3.0
결국 HTTP/2.0은 TCP 기반 위에서 동작하기 때문에 문제가 발생한다.
따라서 Google은 SPDY 프로토콜에 이어서 새로운 UDP 기반 프로토콜인 QUIC를 고안하게 된다.
즉, HTTP/3.0은 TCP 기반이 아닌 UDP 기반으로 동작하는 프로토콜이라고 보면 된다.

HTTP 3.0은 HTTP 2.0이 가지는 장점들을 모두 챙기면서 TCP 기반 이기에 존재하는 단점들을 보완하는 것을 중점으로 개발되었다.
클라이언트와 서버 사이의 RTT를 거의 제로 수준으로 만들었고, 패킷 손실에 대한 빠른 대응과 사용자 IP가 바뀌어도 연결이 유지되는 것이 특징이다. 이러한 특징들을 자세하게 알아보자.
QUIC

UDP는 다음과 같이 헤더가 매우 간단한 구조로 되어있다.
다르게 말하자면, 데이터 전송에 필요한 기본적인 뼈대로 UDP 위에 커스터마이징이 가능하다는 점이다.

UDP 기반으로 만든 QUIC는 TCP의 무결성 보장 알고리즘과 TLS 기능을 탑재하여 성능과 동시에 신뢰성을 충족시켰다.
그래서 계층 위치도 저런식으로 Application과 Transport 사이에 걸쳐서 표현이 된다.
RTT

QUIC는 통신을 시작할 때 3-way handshake 과정을 거치지 않아도 되기 때문에 첫 연결 설정에 단 1 RTT만 소요된다.
QUIC는 자체적으로 TLS 인증서를 내포하고 있기 때문에, 최초의 연결 설정에서 필요한 인증 정보와 데이터를 함께 전송한다.
그래서 클라이언트는 server에게 신호를 한번 주고 바로 통신을 시작할 수 있게 된다.
위의 그림에서 볼 수 있듯, 기존의 TCP + TLS 기반 초기 연결 설정에는 여러번 handshake 과정이 발생하게 된다.
최초의 요청을 보낼 때는 클라이언트는 서버의 세션 키를 모르는 상태이기 때문에, 목적지 서버의 Connection ID를 사용하여 생성한 특별한 키인 초기화 키(Initial Key)를 사용하여 통신을 암호화 한다.

추가적으로 한번 연결에 성공했다면 그 설정을 캐싱해놓고 있다가 다음 연결 때 바로 연결을 하기 때문에 0 RTT 만으로 통신을 시작한다.
Connection ID

QUIC는 Connection ID를 사용하여 서버와 연결을 생성한다.
Connection ID는 랜덤한 값이라서 클라이언트 IP와는 전혀 무관한 데이터이다. 때문에, 클라이언트의 IP가 변경되도 기존의 연결을 계속 연결을 유지할 수 있다. 따라서, 인터넷 환경에 중간에 바뀌더라도 계속 유지할 수 있는 것이다.

똑같은 Connection ID를 계속해서 사용한다면 해커가 네트워크를 통해 사용자를 추적하여 보안 문제가 일어날 수 있다.
따라서, QUIC 프로토콜은 새 네트워크를 사용될 때마다 Connection ID를 변경한다.
서버에서는 해당 Connection ID를 변경하더라도 이전 Connection ID와 동일하다고 인지하여 연결을 유지한다고 한다.
문제점
- UDB 트래픽은 ICMP 공격에 주로 사용되기 때문에 많은 서비슫르에서 차단하거나 속도를 제한하고 있기 때문에, 해당 UDP 기반의 QUIC 프로토콜을 사용하는 것을 꺼리고 있다.
- QUIC는 패킷별로 암호화를 진행하기 때문에 기존의 패킷들을 묶어서 암호화 하는 방식보다 오버헤드가 더 클 수 있다는 우려가 있다.
- HTTP/1.1 이나 HTTP/2 기반으로 이미 최적화를 끝낸 기업들은 새로운 프로토콜을 도입하는 것에 부담을 느낄 수가 있다.
참고 문헌
https://www.baeldung.com/cs/http-versions
HTTP의 진화 - HTTP | MDN
HTTP (HyperText Transfer Protocol)은 월드 와이드 웹의 기반이 되는 프로토콜입니다. 1989년부터 1991년까지 Tim Berners-Lee와 그의 팀이 개발한 HTTP는 유연함을 형성하는 동시에 단순함을 지키는 데 도움이
developer.mozilla.org
HTTP/2, 제대로 이해하기
HTTP? HTTP는 인터넷을 사용해봤다면 누구나 한 번쯤 들어 봤을텐데요. HyperText Transfer Protocol의 약자로, HyperText(링크를 통해 다른 문서로 연결될 수 있는 문서)를 Transfer(전송하는) Protocol(규격이 정
gngsn.tistory.com
HTTP/2 소개 | Articles | web.dev
HTTP/2 (또는 h2)는 웹에 푸시, 다중화 스트림 및 프레임 제어를 제공하는 바이너리 프로토콜입니다.
web.dev
🌐 HTTP 2.0 소개 & 통신 기술 알아보기
HTTP / 2.0 HTTP 2.0은 기존 HTTP 1.1 버전의 성능 향상에 초점을 맞춘 프로토콜이다. 인터넷 프로토콜 표준의 대체가 아닌 확장으로써, HTTP 1.1의 성능 저하 부분과 비효율적인 것들을 개선되어 탄생한
inpa.tistory.com
🌐 HTTP 3.0 소개 & 통신 기술 알아보기
HTTP / 3.0 HTTP 2.0 의 등장과 함께 기존의 프로토콜 데이터 체계를 프레임과 스트림 개념으로 재구축한 결과 기존 보다 혁신적으로 성능이 향상되게 되었다. 하지만 여전히 HTTP는 TCP 기반 위에서 동
inpa.tistory.com