HTTP 1.1의 문제점
HoL Blocking
HTTP 는 TCP 위에서 구현된다. TCP는 신뢰성 있는 연결을 보장하는 프로토콜이다. 따라서, 연결과 해제 과정에서 확인 절차를 위해 많은 네트워크 통신이 발생한다. 즉, 한번의 TCP 연결에서 많은 데이터를 주고 받아야 TCP 연결과 해제 비용을 아낄 수 있다.
HTTP/1.1 은 HTTP 1.0이 한번의 TCP 연결에서 한번의 HTTP 요청과 응답만 처리하는 것을 개선하기 위해 파이프 라인 (큐)를 사용해 여러 HTTP 요청을 순차적으로 전송하고, 이에 대한 응답 역시 순차적으로 받을 수 있게 했다.
Head of Line Blocking = “맨 앞 차는 뭐하는거야?!
”
고속도로에서 길이 막히는 것 = HoL Blocking
HTTP/1.1에서는 여러개의 HTTP 요청을 하나의 TCP 연결에서 보낼 수 있지만, 요청 된 순서대로 그 응답을 받아 처리해야 하는 제약이 있다. 따라서, 먼저 보낸 HTTP 요청이 어떤 이유로 인해 처리하는데 오래 걸리면, 뒤의 요청이 이를 기다려야 한다.
이를 타계하기 위해 비교적 크기가 큰 이미지들을 하나로 붙인 이미지를 요청하고, 브라우저에서 스크립트 코드나 CSS를 이용해 잘라서 사용하는 등 별의 별 방법을 동원해 사용했다.
실제 네이버에 접속하면 가져오는 이미지
HTTP/2
HTTP/2의 목표
HTTP/2 는 처리량의 증가와 하위 호환을 목표로 했다.
HTTP/2는 앞서 설명한 HTTP/1.1의 문제점을 해결해, 더 짧은 시간에 더 많은 요청을 처리할 수 있어야 하면서도, 사용 편의성을 위해 제공되는 기능이나 핵심 개념(예: HTTP 메서드, 상태 코드, URI, 헤더 필드)은 변경되지 않아야 했다.
이런 목표를 달성하기 위해 HTTP 2는 새로운 레이어를 도입했다. 바이너리 프레이밍 레이어가 바로 그것이다.
바이너리 프레이밍 레이어
HTTP 2에서는 기존의 텍스트 기반 메세지를 압축해 각각의 HTTP 요청이 더욱 작은 크기로 구성되도록 했다. 이는 전송 계층(TCP)를 통해 데이터가 전송 될 때 압축되어 전송되는 것을 의미하기 때문에, 기존의 헤더나 본문 등을 그대로 사용할 수 있다. 이런 압축을 위해 바이너리 프레이밍 이라는 계층이 추가되었다.
기존의 HTTP 통신은 바이너리 프레이밍 레이어를 통과해 더 작은 메세지와 프레임 이라는 단위로 쪼개져 TCP 프로토콜을 통해 전송된다.
스트림, 메세지, 프레임
바이너리 프레이밍 레이어의 도입과 함께 추가된 새로운 개념들이다.
메세지
요청과 응답을 표현하는 단위다. HTTP/1.1 에서의 메세지와 동일한 의미이며, 하나 이상의 프레임으로 구성된다.
프레임
HTTP 2에서 사용하는 가장 작은 데이터 전송 단위이다. 각 프레임에는 길이, 프레임 헤더, 프레임 타입과 플래그, 스트림 구분자, 그리고 페이로드가 포함된다.
프레임 구조
프레임 타입에 따라 그 구조와 역할이 다르다. 가령 DATA 프레임은 HTTP 요청과 응답의 페이로드를 표현하고 전달하는데 사용하고, HEADERS 프레임은 스트림을 열고 닫거나, HTTP 헤더를 전달하는데 사용한다.
스트림
독립적으로 전달되는 프레임의 양방향 흐름. 하나 이상의 메세지를 전달할 수 있다.
스트림과 메세지 그리고 프레임 사이의 관계
하나의 HTTP 2 연결은 여러 스트림으로 구성된다. 각 스트림은 여러 메세지를 전송한다. 메세지는 실제로 여러 개의 프레임이라는 단위로 분할되어 전송된다. 한 쪽에서 전송된 프레임은 같은 메세지를 구성하는 프레임들이 모두 도착하면 다시 병합되어 해석된다.
스트림과 메세지, 그리고 프레임의 관계. 출처 :https://web.dev/articles/performance-http2?hl=ko
기존의 HTTP 통신 데이터를 바이너리 형태로 분할 및 압축한 프레임을 스트림이라는 논리적 흐름으로 묶어 통신하는 것이 HTTP/2 다.
HoL 블로킹 해결
스트림은 실제로 논리적인 흐름이고, 실제로 프레임은 한번에 하나 씩 전송된다. 기존과는 다르게 다른 스트림에 속한 프레임을 번갈아가며 전송할 수 있으므로, 병렬 처리가 가능해진다.
HTTP/2 의 HoL 해결 출처 :https://web.dev/articles/performance-http2?hl=ko
예를 들어 클라이언트가 1번 스트림의 데이터 프레임을 처리하는 동시에 5번 스트림의 데이터를 보낼 수 있다. 이런 스트림들은 모두 하나의 TCP 연결 위에서 전송되기 때문에 응용 계층 레벨에서 HoL 블로킹은 자연스럽게 해결된다. 오래 걸리는 요청(메세지)를 기다리지 않고 다른 요청(메세지)의 일부인 프레임을 송수신 할 수 있기 때문이다.
우선순위 기능
여러 HTTP 요청이, 동시에 여러 스트림을 통해 서버에 전달될 수 있으므로, 동시에 도달한 요청을 서버가 어떤 순서로 처리하느냐에 따라 서버와 클라이언트 모두에게 중요한 성능 문제를 야기할 수 있다. 예를 들어, CSS 파일을 요청하는 스트림이 HTML 문서를 요청하는 스트림보다 먼저 처리된다면, 브라우저는 HTML 문서가 아직 없으므로 아무것도 할 수 없다. 만약, HTML 문서를 요청하는 스트림이 먼저 처리된다면 브라우저는 CSS 파일이 도착하기를 기다리면서 HTML 문서를 분석해 DOM을 만들어, 렌더링을 할 수 있다.
우선순위 부여를 위해 스트림에는 1 ~ 256 범위의 정수를 가중치로 할당할 수 있다. 또한, 순서대로 처리를 하기 원하는 스트림들에게 종속 관계를 표시해 상위의 스트림이 먼저 처리되도록 할 수 있다.
위와 같은 상황에서, D 가 C 보다 먼저 서버의 자원을 할당받아야 하고, C가 A와 B 보다 먼저 서버의 자원을 할당받아야 한다. C와 E 는 같은 비율로 서버의 자원을 할당받고, A와 B는 A 가 B 보다 3배 더 많이 서버의 자원을 할당받아야 한다.
강제가 아니라 권고다
사실, 이런 우선순위나 종속 설정은 강제적인 것이 아니라 “서버님 이렇게 해주시길 바랍니다.” 정도의 강한 권고다. 따라서, 실제로 위 설정대로 처리가 되는 것을 보장하지는 않는다.
이는 서버 입장에서, 우선순위가 높은 요청을 처리하는 것이 너무 오래 걸릴 경우, 그동안 다른 요청을 처리하지 않는 것은 자원 낭비이기 때문이다.
같은 출처, 같은 TCP 연결
앞서 말한 대로 HTTP/1.1 에서는 하나의 TCP 연결을 재사용 하기 위해서는 먼저 보낸 HTTP 요청에 대한 응답이 오기를 기다려야 했다. 하지만, 웹 환경이 점점 더 커지고, 빈번히 HTTP 요청이 요구되도록 발전하면서 이런 방식으로는 대응할 수 없게 되었다. 따라서, 웹 브라우저는 아예 TCP 연결을 여러개 만들어서 HTTP 요청을 동시에 보내게 되었다.
HTTP/2 에서는 여러 스트림을 병렬로 처리하는 것이 가능하기 때문에, 여러 TCP 연결을 사용할 필요가 없어져 자연스럽게 TCP, TLS 프로토콜의 오버헤드를 줄여 더 효율적인 통신을 가능하게 했다.