본 글은 그림으로 공부하는 TCP/IP 구조 책을 바탕으로 작성하였습니다.
Hypertext Transfer Protocol의 약자.
애플리케이션 계층에서 사용되는 프로토콜로 오늘날 인터넷 세상에서 가장 많이 사용되는 프로토콜이다.
주요 변화를 중심으로 알아보자.
기존에는 텍스트 파일만 다운 가능했지만 1.0 버전에 이르러 다양한 파일을 다룰 수 있게 되었으며, 이 시점을 기준으로 요청/응답 구조도 확립되고 현재까지 이어져 http의 토대가 되었다.
keep-alive, 파이프라인 등 TCP 레벨에서 퍼포먼스 향상을 목표로 하는 기능이 추가되었다.
그러나 파이프라인 기능의 경우 HoL 블로킹이 원인이 되어 브라우저에서는 기본적으로 비활성화 되어있다.
HoL 블로킹 문제란?
Head Of Line Blocking의 약자로 http와 tcp 두 프로토콜에서 발생할 수 있다. 먼저 http의 HoL 문제의 경우

다음과 같이 3개의 이미지를 보낸다고 가정했을 때,

위 상황처럼 앞의 전송이 지연된다면 이후 전송 과정도 영향을 받게 된다.
http 1.1에서 지원하는 파이프라인 기능의 경우도 전송은 앞 통신의 응답을 기다리지 않고 보내지만, 결국 순서대로 응답을 받아야 하기 때문에 지연이 발생하는 것은 똑같다.
TCP에서의 HOL Blocking은 HTTP 요청/응답을 TCP 패킷 레벨로 바꾼 거라고 생각하면 된다.
TCP는 패킷을 전송할 때에, 전달을 보장하기 때문에 패킷이 손실되면 재전송하게 된다.
그리고 재전송이 발생하면 패킷의 순서가 역전되지 않도록 후속 패킷이 대기하게 된다.
즉, TCP 상에서 3개의 패킷을 보낼 때, 먼저 보낸 패킷에서 손실이 발생하면 뒤도 막히게 된다.
이것이 TCP에서의 HOL Blocking 이다.
http/2 에서는 http 단에서 발생하는 HoL 문제를 스트림이라는 개념을 활용해 해결하였다. 즉 요청 별로 별개의 스트림을 사용하여 앞의 패킷에 대한 응답이 도착하지 않더라도 뒤의 통신이 응답을 받을 수 있는 것이다.
그러나, TCP에서의 HoL 문제는 여전히 남아있기 때문에 완전히 HoL 블로킹 문제에서 벗어난 것이라고 볼 순 없다.
이와 관련하여 구글에서 개발한 QUIC 프로토콜은 HTTP, TCP 단에서의 HoL 문제를 전부 해결하는 형태로 프로토콜을 개발하고 있다. 관련 자료는
을 참고하자.
구글이 개발한 SPDY라는 프로토콜을 기반으로 2015년 표준화되었다.
데이터를 프레임 단위로 교환하며 멀티 플렉싱, HPACK, 서버 푸시 등 TCP 레발뿐 아니라 애플리케이션 레벨에서도 성능 향상을 목표로 하는 기능들이 추가되었다.
http/1.1의 파이프라인 기능이 큰 효과를 거두지 못해 대신 추가된 기능으로
TCP 커넥션 안에 스트림이라는 가상의 채널을 만들고, 스트림 별로 요청과 응답을 교환함으로써 HoL 블로킹 문제를 해소했다.
메시지 헤더를 압축하는 기능, 자주 사용하는 헤더 이름이나 헤더를 미리 정적인 숫자로 치환하는 등의 일 수행
클라이언트가 최초로 요청한 컨텐츠를 해석하고, 다음에 올 요청에 대한 응답을 요청이 오기 전에 보내는 기능
예를 들어 서버에 index.html을 요청했다고 가정하면, 서버에서 그 다음 요청이 style.css, script.js를 요청할 것을 예상하고 이를 함께 응답으로 내려준다.
클라이언트는 이 응답을 캐시해두고 다음 style.css, script.js 요청을 보낼 때 캐시에서 응답을 가져올 수 있다.
구글이 개발한 QUIC 프로토콜을 기반으로 가장 큰 특징은 TCP 기반이 아닌 UDP 기반의 프로토콜이다. 속도 향상에 큰 효과가 있을 것으로 예상된다.
HTTP/1.1과 HTTP/2의 메시지 포맷은 엄청난 차이가 있지는 않다. 또한 전체적인 포맷 양식만 확인하고 세부적인 필드를 모두 이해하고 외우고 있는 것은 비효율적이라 생각한다. 여기서는 포맷 정도만 확인하자.

요청 http의 경우
응답 http의 경우
HTTP/1.1은 데이터를 텍스트 형식으로 보내기 때문에 중간에 바이너리 형식으로 변환하는 작업이 필요하지만, HTTP/2의 경우 바이너리 형태로 데이터를 주고받는다.
또한 HTTP/2는 메시지 헤더를 HEADERS 프레임에, 메시지 바디를 DATA 프레임에 각각 분할해서 저장하고 바이너리 형식의 프레임 단위로 스트림에 보낸다.
그리고 그 프레임에 스트림을 식별하는 스트림 ID를 부여해서 어떤 스트림에 프레임ㅇ르 보내는지 지정한다.