해당 단원에서는 HTTP/2.0에 대해서 다루고 있습니다. 하지만, 여기서 정리할 것은 HTTP/1.0, HTTP/1.1, HTTP/2.0 그리고 HTTP/3.0까지 정리를 해볼 예정입니다.
여기서는 시간순으로 정리를 하면서, 이전 버전에 어떠한 기능들이 있었고 이것의 문제점을 파악하고 어떻게 개선된 형태로 다음 버전이 등장하였는지에 대해 초점을 맞출 것입니다.
먼저, HTTP에 대해서 다시 한번 정리하고 들어갑시다.
HTTP(HyperText Transfer Protocol)는 월드 와이드 웹(WWW)에서 데이터를 주고받기 위한 프로토콜입니다. 이 프로토콜은 웹 브라우저와 웹 서버간에 통신을 가능하게 합니다. HTTP의 주요 특징과 구성 요소들은 다음과 같습니다.
주요 개념(특징)
주요 요소
초기 HTTP인 0.9는 결함을 많이 가지고 있었습니다.
본래 HTTP/0.9는 간단한 HTML 객체를 받아오기 위해 만들어진 것입니다.
문서를 주고 받기 위해선 SMTP, FTP, Telnet과 같은 것을 사용해야 됐습니다.
이것에 불편함을 느껴서 등장하게 된 것이 WWW(World Wide Web)이였고, 표현 방식으로는 HTML을 사용한 것이죠.
웹이 정상적으로 동작하기 위해서는 통신 방식을 규격화할 필요가 있었는데, 이를 위해 선택한 프로토콜이 HTTP였습니다. 이것을 목표로 빠르게 성장하게 됩니다.
HTTP/1.0이 등장하게 되는데, 이것은 처음으로 널리 쓰이기 시작한 HTTP 버전입니다.
HTTP/1.0은 이전 버전과 다르게 다음과 같은 것들이 추가되었습니다.
추가된 것
요즘 사용하는 기능들이 추가된 것으로 보았을 때 굉장히 잘 나온 것 같지만 그렇지 않습니다.
급성장하던 시기에 만들어진 잘 동작하는 용례들의 모음에 가깝고, HTTP/1.0은 결코 잘 정의된 명세라고 보기 힘들었습니다.
월드 와이드 웹이 급성장하게 되면서 유명 웹 클라언트와 서버 들은 그에 따른 요구를 만족시킥 위해 발빠르게 HTTP에 기능을 추가했습니다.
HTTP/1.1은 HTTP 설계의 구조적 결함 교정, 두드러진 성능 최적화, 잘못된 기능 제거에 집중했습니다.
뿐만 아니라 HTTP/1.1은 더 복잡해진 웹 애플리케이션과 배포를 지원합니다.
요즘 같이 기능들이 빠르게 추가되는 시대에서 보면 이러한 성장 속도가 빠른 것인지 의문이 들 수 있지만, HTTP는 굉장히 신중하게 설계되고 성장해온 케이스로 하위 호환성을 유지하기 위해서 굉장히 노력했습니다.
HTTP/1.1에서는 다음과 같은 기능이 추가되었습니다.
여기서 살펴볼 것은 병렬 커넥션, 지속 커넥션과 파이프라이닝을 살펴볼 것이고 이것이 가지고 있는 장단점을 살펴볼 것입니다.
먼저, 기존에 전송 방식을 살펴보면 하나의 커넥션에서 송신자가 요청에 대한 응답을 받기 전까지는 다음 요청을 보낼 수 없습니다. 이것은 회전 지연(latency)을 발생시키게 됩니다.
심지어, TCP Connection을 맺어야 요청들이 2개 이상인 상황에서 하나의 커넥션만 사용할 수 있는 경우에는 이것으로 인한 지연이 높을 것입니다.
이것을 위해서 병렬 커넥션이 등장합니다.
병렬 커넥션은 여러 개의 커넥션을 동시에 맺음으로써 TCP Connection으로 발생하는 지연을 줄여줍니다.
하지만 해당 방식은 다음과 같은 문제점을 가지고 있습니다.
이에 따라 적은 수(대부분 4개)의 병렬 커넥션만 허용합니다.
지속 커넥션의 경우, keep-alive가 가지고 있던 멍청한 프록시 문제라던가 해당 기능을 사용하기 위해서 헤더에 Connection:keep-alive를 추가해야 된다 점이나 확장 기능이였다는 문제점들을 해결하기위해 등장한 것입니다.
이것과 같이 사용할 수 있는 것이 바로 파이프라이닝(Pipelining)입니다.
파이프라이닝은 하나의 지속 커넥션을 통해 여러 요청들을 연속적으로 전송할 수 있도록 해주는 기능입니다.
여기서 중요한 점은, 요청들을 연속적으로 전송하고 나서 이에 대한 응답은 순차적으로 이루어져야 한다는 점입니다.
HTTP 메시지는 순번이 매개져 있지 않아서 응답이 순서 없이 오면 순서에 맞게 정렬시킬 수 있는 방법이 없기 때문입니다.
이로 인해서 발생하는 성능 문제가 'HOL-Blocking'(Head of Line Blocking)입니다.
여기까지 정리를 해보자면,
HTTP/1.1은 요청/응답 지연 문제를 해결하기 위해 병렬 커넥션과 파이프라이닝 기능을 추가하였습니다.
But, 병렬 커넥션은 커넥션 수에 제한이 있고, 파이프라이닝은 'HOL-Blocking'문제가 발생한다는 것을 확인했습니다.
이러한 성능 문제를 개선하기 위해서 HTTP/2.0에 나온 대표적인 기능이 "Multiplexing"입니다.
HTTP/2.0에는 스트림(Stream)이란 개념이 도입되는데 이것은 하나의 커넥션에서 여러 요청과 응답을 처리할 수 있도록 해줍니다.
하나의 커넥션에는 여러 스트림이 생성될 수 있고, 하나의 스트림에는 한 쌍의 요청과 응답을 처리할 수 있습니다.
HTTP/2.0의 대표적인 기능들에 대해서 알아봅시다.
HTTP/2 주요 기능
하나씩 좀 더 구체적으로 정리를 해보도록 하겠습니다.
먼저 기존에는 텍스트 기반 프로토콜이였기 때문에 인코딩을 한다면 ASCII 코드로 변환을 하여 전송을 했습니다. 바이너리 프로토콜의 도입으로 실제 내용보다 더 압축하여 전송이 가능하게 되었습니다.
HTTP/2에서는 모든 메시지가 프레임에 담겨 전송되는데, 프레임의 구조에서 중요한 것은 스트림 식별자와 스트림 페이로드라고 볼 수 있습니다.
모든 스트림은 31비트의 무보호 정수(unsigned integer)로 된 고유한 식별자를 갖는데 이것을 통해 커넥션에서 생성되는 스트림을 구별할 수 있는 것이고 바이너리 데이터는 페이로드에 담긴다고 볼 수 있습니다.
동시에 여럴 개의 스트림을 사용하면 스트림이 블록될 우려가 있는데, 이것은 WINDOW_UPDATE
프레임을 이용한 흐름 제어(flow control)를 통해, 스트림들이 서로 간섭해서 망가지는 것을 막아줍니다.
이렇게 하나의 커넥션 안에서 여러 스트림을 사용할 수 있게 해주면서 Multiplexing 기능을 가능케 해주었습니다.
서버 푸쉬의 필요성의 경우, 예시를 한번 살펴보면 좋을 것 같습니다.
HTML 문서를 요청 받은 서버는 그 HTML 문서가 링크하고 있는 이미지, CSS 파일, 자바스크립트 파일 등의 리소스를 클라이언트에게 푸시할 수 있는 것이다. 이는 클라이언트가 HTML 문서를 파싱해서 필요한 리소스를 다시 요청하여 발생하게 되는 트래픽과 회전 지연을 줄여줍니다.
이 과정에서 PUSH_PROMISE
프레임을 사용하는데 이것을 통해 리소스를 푸시하려는 서버는 클라이언트에게 자원을 푸시할 것임을 알려줍니다.
이것은 하나의 스트림에서 이루어지고, 만약 클라이언트가 원하지 않는다면 RST_STREAM
프레임을 사용해서 거절할 수 있습니다.
이 기능이 동작하는 동안에는 클라이언트는 해당 리소스를 별도로 요청하면 안됩니다.
헤더 압축과 관련해서는 HPACK이라는 특별한 방식으로 헤더를 압축한다고 했고, 여기서 중요한 점이 하나 있는데 헤더와 데이터를 별도의 프레임으로 구분하여 전송한다는 점입니다.
이것은 헤더와 데이터의 전송을 병렬 전송 방식으로 보낼 수 있도록 해줍니다.
HTTP/2만 보더라도 꽤 좋은 기능들을 가지고 있는 것 같다.
그렇다면 HTTP/3은 HTTP/2의 어떠한 단점을 보완하기 위해 나온 것 일까요?
HTTP/2의 단점은 다음과 같습니다.
HTTP/2의 주요 단점
좀만 더 정리해보자.
TCP 자체의 헤드 오브 라인 블로킹 문제는 무엇을 말하는 것일까?
네트워크 환경이 변경되면 TCP 재연결?
이것은 네트워크 환경의 변화로 인한 IP 주소 변경 때문입니다.
이러한 문제들을 개선하기 위해 HTTP/3이 등장합니다.
HTTP/3은 이야기해야 되는 부분이 다소 많을 것 같습니다.
해당 내용은 다음 페이지에서 정리를 해뒀습니다.
탑재된 기능만 정리하자면 다음과 같습니다.