HTTP/0.9에서 HTTP/3까지의 여정

mylime·2024년 12월 10일
2

이 포스팅은 2024.12.08에 작성되었습니다.



서론


이전에는 HTTP가 통신 프로토콜이라고만 흐릿하게 알고있었는데, 네트워크를 공부하면서 꽤 내용이 깊다는 걸 알게되었다. 그리고 같은 HTTP라고 불려도 버전에 따라 차이가 큰 경우도 많았다.(특히 HTTP/3의 경우 다른 버전들과는 너무 달랐다) 웹 개발자가 되기 위해서 HTTP는 필수로 알아야하는 내용이라고 생각한다. 이번 기회에 확실하게 잡고 넘어가려고 한다!

또한 HTTP 버전 별 업데이트된 사항의 이론을 위주로 공부하기보단, 역사를 공부하듯이 흐름을 공부하면서 어떤 필요에 의해서 어떻게 발전해나갔는지 위주로 공부해보려고 한다.



HTTP란?


HTTP는 HyperText Transfer Protocol의 약자이다. 클라이언트가 웹페이지를 요청하고 응답을 받는 형식이라고 생각하면 되겠다.

HTTP는 처음에 HTML을 주고받기 위한 목적으로 만들어졌고, 이후 이미지와 비디오, api, 파일, web-based 서비스도 제공받을 수 있게 발전해나갔다.



1996년 - HTTP/0.9의 등장


HTTP/0.9는 클라이언트가 웹 페이지에 대한 요청을 보내면, 서버는 HTML파일을 응답해주는 간단한 프로토콜

가장 초기버전인 HTTP/0.9는 헤더도 없고, status code도 없이 그냥 HTML파일을 받는 용도로 사용되었다.

이후에 HTTP/0.9는 header요청 내부 body, status code를 추가하고, 새로운 method(POST, HEAD)도 추가하였다.


데이터 요청 시 많은 요청&응답이 필요한 단점

HTTP/0.9은 요청 하나 당 하나의 connection이 필요하다. 요청 하나를 처리한 후, 해당 connection을 계속 닫아준다. 이런 설계는 데이터를 주고받는 과정에서 많은 요청&응답이 필요하다는 단점이 있다.

connection을 열고닫기위해 TCP handshaking이 필요하고, https를 사용한다면 TLS handshaking도 부가적으로 필요하다. 요청마다 이런 오버헤드가 발생한다면 비효율적이겠다.



1997년 - HTTP/1.1의 등장


HTTP/1.1은 고속화와 안정성을 추구한 확장이다

HTTP/1.1은 25년이 지난 현재도 여전히 사용되는 프로토콜이다.HTTP/1.1http/0.9의 문제를 해결하고자 하였다.


1. (HTTP/1.1) persistent connections

persistent connections은 하나의 요청마다 connection을 닫지 않고, 연속된 요청에 대해 이전 connection을 재활용할 수 있는 방법이다. TCP 재연결을 하지 않기 때문에 handshake 횟수가 줄어들고, https 통신의 경우 TLS handshake도 생략되어 속도가 빨라질 수 있다. persistent connection을 Keep-Alive라고 부른다.

사실 Keep-Alive는 HTTP/1.1 이전에도 몇몇 브라우저에서 지원하던 기능이었고, HTTP/1.1이 나오면서 기본으로 설정이 되게 되었다. (자세한 내용은 이전에 블로그에 포스팅한 내용이 있으니 궁금하면 참고하면 좋겠다!)


2. pipelining

하나의 TCP connection으로 여러 개의 request를 보낼 수 있는 기능

클라이언트가 어떤 request에 대한 response를 받은 후에 request를 보내야한다면 효율이 떨어진다. 특히 웹 페이지에 포함된 여러 이미지를 모두 가져와서 뿌려야하는 상황이라면 정말 비효율적일 수 있다.

HTTP/1.1은 response를 기다리지 않고, 바로 요청을 보낼 수 있는 파이프라이닝 기능을 도입하였다. 다음 요청까지의 대기 시간을 없앰으로써, 성능을 향상시킨다. (물론 파이프라이닝은 Keep-Alive 이용을 전제로 한다)


3. chunk transfer encoding

server는 response를 작은 chunk로 쪼개서 보낼 수 있다

HTTP/1.1에서 스트리밍 다운로드/업로드 방식이라고 불리는 청크기반 통신이 도입되었다. 청크를 기반으로 쪼개서 보냄으로써 시간이 오래 걸리는 데이터 전송을 조금 더 앞당겨 수행할 수 있게 되었다.

client 측면에서는 전체 response가 ready될 때까지 기다리지 않아도 되기 때문에 초기 page rendering이 빨라질 수 있고, 특히 용량이 크거나 변동이 많은 content의 경우 사용자 경험이 더 증대될 수 있다.

서버 측면에서도 전송에 필요한 블록만 메모리에 로드하여 TCP 소켓이 실어보낼 수 있기 때문에 메모리 부담이 줄어들 수 있다.


4. caching & conditional requests

Cache-Control, ETag, If-Modified-Since 헤더를 추가

Cache-ControlETag를 이용하면 content 캐싱이 용이해지고, 불필요한 데이터 전송을 줄일 수 있다.


If-Modifed-Since 헤더를 이용하면 클라이언트는 바뀐 리소스에 대해서면 요청을 보낼 수 있게 된다. 서버는 해당 헤더의 값과 서버의 콘텐츠 일시를 비교하여 변경되었을 경우에만 body값을 보낸다. 이를 통해 대역폭을 아낄 수 있게 되었다.


+) TLS의 규격화

HTTP/1.1과 병행해 통신 경로를 암호화하는 TLS가 규격화되었다.



HTTP/1.1 의 문제점


1. Head-of-line blocking(HOL 블로킹)

파이프라인 내의 첫 번째 request가 지연되면, 나머지는 기다려야하는 문제

HTTP/1.1에서 파이프라이닝이 도입되면서 성능향상을 기대하였지만, 웹사이트가 커지고 복잡해지면서 Head-of-line blocking(HOL 블로킹) 문제가 생겼다. 해당 문제와 다른 이슈 등으로, 결국 많은 브라우저들은 pipelining을 쓰지 않게되었다.

개발자들은 HOL 블로킹 이슈를 극복하기 위해 다음과 같은 방법을 사용하였다.


1-1. domain sharding

웹사이트는 subdomain에 static asset을 제공한다

각 subdomain마다 6개의 connection이 더 추가되었음


1-2. few request by bundling assets

웹사이트는 subdomain에 static asset을 제공하는 방식이다. 이미지는 sprites css를 사용하여 결합되고, js파일도 연결된다



2015 - HTTP/2의 등장


HTTP/2는 HTTP/1.1의 성능적 문제를 해결하기 위하여 설계되었다


1. binary framing layer

HTTP/1.1은 plain text message를 사용하여 통신하였는데, HTTP/2는 binary format message를 사용한다. 요청과 응답을 frame이라고 불리는 작은 unit으로 나누고, frame을 stream을 통해 바이너리 데이터를 다중으로 송신할 수 있게 되었다.


2. full request and response multiplexig

HTTP/1.1까지는 하나의 요청이 TCP 소켓을 독점하는 구조였다.

HTTP/2에서는 하나의 TCP 접속 안에 stream이라는 가상의 TCP 소켓을 만들어 통신한다. HTTP/2의 요청 및 응답은 청크가 frame 단위로 분할되어 있고, 각 frame들은 독립적이다. 그래서 frame들은 전송 중에 혼합될 수 있고, 반대쪽에서 다시 조립될 수 있다.

그렇기 때문에 HTTP/2에서는 ID값과 TCP 통신 용량이 허락하는 한, 손쉽게 몇 만번의 접속이라도 병렬화 가능하게 되었다. 덕분에 통신 속도가 빨라졌고, HTTP/0.9의 문제인 HOL 블로킹 문제도 해결할 수 있었다.


3. stream prioritization

stream prioritization을 통해 요청의 우선순위를 결정할 수 있게 되었다.

웹페이지의 경우 assets을 로딩하는 순서가 중요한데, 이 기능을 이용하여 브라우저가 요청의 우선순위를 결정 가능하다. 클라이언트가 서버에게 우선순위를 알려주면, 서버는 중요한 요청에 대해 더 많은 frames을 보내준다.



4. server push

클라이언트 요청하기 전, 필요한 리소스를 미리 response로 보내는 방법

server push를 이용해 우선순위가 높은 콘텐츠를 클라이언트가 요구하기 전 전송이 가능해졌다
즉, 서버는 요청받은 HTML 페이지와 함께 추가 리소스를 담아 보낼 수 있다!

server push는 어디까지나 CSS, js, image 등 웹 페이지를 구성하는 파일을 다운로드하는 용도로 이용되며, 클라이언트가 요청을 보낼 때까지 데이터가 서버 쪽에서 푸시된 것을 감지할 수 없다.

푸시된 컨텐츠는 사전에 캐시에 들어가고, 콘텐츠가 캐시에 들어간 뒤에 클라이언트가 해당 파일 요청 시, 곧바로 다운로드 할 수 있는 것처럼 보이게 되는 원리이다.


5. header compression

HTTP/1.1에서는 메인 데이터만 압축이 가능하였고, 헤더는 plain text로 보냈다.

HTTP/2에서는 HPACK 압축을 통해 header의 크기를 줄일 수 있다. 헤더를 줄일 뿐만 아니라, 이전 헤더도 기억하여 이후의 헤더를 더 압축 가능하도록 구현되었다.



2022 - HTTP/3의 등장


HTTP/3 used quic instead of TCP

TCP의 특성은 패킷손실 및 head-of-line 블로킹으로 인해 발생하는 낮은 페이지 처리율을 가진다는 것이다. latency가 높거나, 손실이 많은 네트워크일 경우 더 성능이 낮아진다.

HTTP/3은 TCP를 사용하지 않음으로써 이 문제를 해결한다. 이를 QUIC 프토토콜이라고 한다.


구글이 만든 QUIC 프로토콜은 UDP 기반으로 구축된 connectionless 프로토콜이다. 이를 통해 얻을 수 있는 장점들은 아래와 같은 것들이 있다.

  • reduce latency
  • faster than tcp
  • handle network changes well

1. reduce latency

TCP의 head-of-line blocking 없이 multiplexing을 향상시킴

QUIC 프로토콜은 패킷 손실을 더 잘 처리가능하다. 또한 connection이 원활하게 변경될 수 있어 특히 모바일 네트워크에서 더 나은 성능을 발휘한다.


http3 연결 과정은 다음과 같이 진행된다.
1. 클라이언트가 서버와 http3 연결을 시도하면 quic handshake가 시작된다
2. quic은 보안을 위해 TCP1.3과 결합되며, TLS 핸드셰이크는 quic connection setup과정 중에 발생한다. (이를 통해 전반적인 latency를 줄일 수 있음)


2. setup connection이 tcp보다 빠름

클라이언트가 서버와 이전에 통신한 적이 있는 경우, quic은 한 번의 round trip으로 connection을 보호할 수 있음

가끔은 zero RTT로 수행하기도 한다. 클라이언트는 바로 request를 보낼 수 있고, 서버는 handshake 없이 처리 가능해진다.


3. handle network changes well

network 변경을 더 잘 처리한다

예를 들어 휴대폰에서 Wi-Fi => cellular 전환하는 경우, http3 connection은 유지된다. 이는 quic이 ip주소에 의존하지 않고, connection id로 처리하기 때문이다.



+) http 버전 별 사용비율

구글링을 해보니, HTTP/3 사용 비율이 많이 증가했다는 걸 알 수 있었다. HTTP/1.1도 여전히 잘 사용되고 있는 걸 확인할 수 있다.



마치며..

역사를 공부하듯이 HTTP를 바라보니 정말 재미있었다. HTTP의 역사는 소프트웨어 엔지니어링 역사의 축소판이라고 하였는데, 단순한 프로토콜로 시작하여 고속화와 안정성을 위해 많은 발전을 이뤄온 걸 보니 어렵게만 느껴지던 HTTP가 더 친숙하게 다가왔다. 처음부터 완벽하게 설계되는 기술은 없는 것 같다는 게 느껴졌다. 이런 재미를 준 Real-World HTTP 도서를 정말 추천한다!!!

아직 궁금한 내용들이 많아서 더 공부해보고, 글을 수정해나가려고 한다.



참고자료

profile
깊게 탐구하는 것을 좋아하는 백엔드 개발자 지망생 lime입니다! 게시글에 틀린 정보가 있다면 지적해주세요. 감사합니다. 이전블로그 주소: https://fladi.tistory.com/

0개의 댓글