HTTP의 버전에 따른 변화
이 포스팅은 해당 내용에 관련되어 질문을 받았을 때 답변하지 못한 경험이 있기에 공부 하기위해 남깁니다.
HTTP란?
HTTP에 대해서 먼저 설명해보도록 하겠습니다.
HyperText Transfer Protocol의 약자로써 웹 서버와 클라이언트 사이에서 문서를 전달하기 위한 프로토콜입니다. 여기서 문서라는 것은 웹페이지를 나타내는 HTML과 CSS 등 리소스를 나타내기도 합니다.
일단 모든 과정에서 동일하게 일어나는 동작 과정은 다음과 같습니다.
연걸(TCP 3-way Handshake)를 진행하고 이후에 클라이언트의 요청에 따라 서버에서는 요구되는 리소스를 전송하고 다시 연결을 해제(TCP 4-way Handshake)합니다.
이 방식에 대해 어떠한 문제가 있었으며 HTTP1/1.1/2는 어떻게 발전되었는지 차례대로 알아보겠습니다.
HTTP1
HTTP1은 가장 쉽게 이해할 수 있는 프로토콜입니다. 클라이언트와 서버가 정보를 서로 전달할 때 과정을 직관적으로 이해할 수 있습니다.
클라이언트와 서버는 서로 데이터를 주고 받을 때 마다 TCP-3-way Handshake를 통해 연결을 하고 전송을 마치면 다시 연결을 해제 하는 방식입니다.
통신을 진행 할 때마다 계속 연결을 진행하기 때문에 지연 시간이 길어지는 문제가 있습니다. 매 통신마다 Handshake 과정이 추가 되기 때문입니다.
이런 문제를 해결하기위해 HTTP1.1로 발전하였습니다.
HTTP1.1
HTTP1.1에서는 HTTP1에서 각 요청마다 Handshake를 통해서 발생하는 오버헤드를 줄이고 싶었습니다. 그래서 한번 연결 한 상태를 유지하며 여러 요청과 응답을 수행하도록 만들었습니다.
HTTP1.1은 Persistent Connection을 지원하면서 한번 연결된 TCP세션을 유지하여 사용하기 시작합니다.
추가적으로 HTTP1의 통신 방식에서는 요청에 대한 응답을 받았을 경우에 다음 요청을 보내는 형식으로 진행이 되었습니다. 하지만 이러한 경우 앞의 데이터가 제대로 전송되지 않았을 경우 이후의 모든 데이터가 전송이 시작되지 않는 경우가 생깁니다.
제대로 요청에 대한 응답이 오게 되더라도 이러한 방식으로 요청/응답이 진행되면 시간적으로 비효율적임을 알 수 있습니다. 요청에 따라 서버의 CPU는 처리를 진행 할 테니까요.
이런 이유로 요청과 응답의 효율을 높이기 위해 파이프라이닝이라는 방식을 사용했습니다.
먼저 전반적으로 HTTP1과 HTTP1.1의 그림을 그려보겠습니다.
이미지를 보면 알 수 있듯이 HTTP1에 비해서 HTTP1.1이 더 효율적인 방법으로 작동합니다. 참고로 파이프라이닝에서 여러 요청을 보낼 수 있는 이유는 각 요청 메세지를 한 TCP소켓을 통해 보내기 때문입니다.
하지만 완벽한 방식은 아닙니다. 데이터는 순차적으로 전송되어야 하기 때문에 응답을 받을 때 순차적으로 받아야 합니다. 만약 이후의 요청이 먼저 끝나더라도 이전의 응답이 전송되지 않으면 미뤄지게 됩니다. 그렇기 때문에 후순위의 응답은 지연되는 문제가 있습니다.
이러한 문제를 Head Of Line(HOL)문제라고 합니다. 간단히 정리하면, 이전 응답이 이뤄질 때 까지 후순위 응답을 지연하는 것 입니다.
또한 추가적으로 버추얼 호스팅이 가능하게 되었다고 합니다. 이 부분은 조금 더 알아보도록 하겠습니다
HTTP2
HTTP2는 HTTP1.1의 요청에 대한 응답의 순서 문제(HOL문제)를 해결하는 방법을 제안합니다.
HTTP1.1의 경우 앞의 요청에 대해 응답이 오지 않는다면 이후의 요청에 대한 결과를 먼저 얻었더라도 순서를 기다리는 문제가 있었습니다. 이 문제를 해결한다면 이전의 결과를 먼저 얻었다면 먼저 응답을 전달하는 방법이라는 것을 알 수 있습니다.
HTTP2는 멀티플렉싱을 이용해서 응답을 최적화하였습니다. 제가 공부를 한 바로는 패킷을 프레임으로 세분화하여 받는 쪽에서 조립할 수 있게 함으로써 순서대로 전달되지 않아도 응답을 할 수 있도록 했다고 알고 있습니다.
출처 : High Performance Browser Networking : O’Reilly
방식은 다음의 그림과 같이 스트림 방식을 이용합니다. 이러한 방식의 결과로 N스트림을 통해 전달 된 요청에 대해서는 N스트림을 통해 응답을 전달하면 됩니다. N개의 스트림은 각각 요청에 대해 응답을 보내주면 되기 때문에 요청이 온 순서대로 일을 처리 할 필요가 없게되는 것 입니다. 그저 요청이 온 스트림을 통해 응답을 주면 되니까요.
스트림을 통해 전달되는 정보는 프레임 단위로 나누어진 여러개의 요청과 응답 정보입니다. 프레임이란 HTTP2에서 사용하는 통신의 최소 단위입니다.
이러한 프레임의 전체 시퀸스는 메세지라고 합니다.
출처 : https://www.alibabacloud.com/blog/understanding-http2-history-features-debugging-and-performance_423733
각 프레임에는 적어도 하나의 프레임 헤더를 가짐으로써 통신하는 스트림에 대한 정보를 가지고 있습니다. 그 외에도 프레임의 길이 등 다양한 정보를 담고 있습니다 자세한 내용은 itchipmunk 님의 블로그를 보시면 더 자세하게 알 수 있습니다. 궁금해서 찾아봤습니다.
참고
https://withbundo.blogspot.com/2021/02/http-http-10-http-11.html
https://m.blog.naver.com/fleshmeat/120041927614
https://brunch.co.kr/@sangjinkang/3
https://www.whatap.io/ko/blog/38/