포스트

[Network] HTTP (1-2) - HTTP/2.0은 무엇이 바뀌었는가?

Computer Science / Network

HTTP/2.0 버전의 특징과 도입된 기술에 대한 개념 정리

HTTP/2.0의 이전 버전인 HTTP/0.9 ~ 1.1 버전에 대한 자세한 설명은 #HTTP (1-1) - HTTP 버전 별 특징 (0.9 / 1.0 / 1.1) 게시글을 참고

HTTP/1.1 → HTTP/2.0 (2015)

HTTP/2.0은 기존 HTTP/1.1 버전의 성능 향상에 초점을 맞춘 프로토콜이다.

인터넷 프로토콜 표준의 대체가 아닌 확장으로써, HTTP/1.1의 성능 저하 부분과 비효율적인 것들을 개선하여 탄생한 것이 HTTP/2.0이라고 보면 된다.

image_2

HTTP/1.1까지는 한 번에 하나의 파일만 전송이 가능했다.

비록 파이프라이닝 기술이 있었지만, 여러 파일을 전송할 경우 선행하는 파일의 전송이 늦어지면 HOL(Head of Line) Blocking이 발생하는 문제가 있었다.

따라서 HTTP/2.0에서는 이 문제를 해결하기 위해 여러 파일을 한 번에 병렬로 전송한다.

image_3

그래서 일반적으로 HTTP/2.0 버전을 사용만 해도 웹 응답 속도가 HTTP/1.1에 비해 15~50% 정도 향상된다고 한다.

위의 이미지는 웹 사이트를 로딩시켜 두 버전의 속도를 비교한 결과이다.

image_4_light image_4_dark

크롬 개발자 도구에서도 프로토콜 확인이 가능한데, 개발자 도구 → 네트워크 탭 → 우클릭 → 헤더 옵션 → 프로토콜 체크를 통해 HTTP 버전을 확인할 수 있다.

http/1.1은 보이는 그대로 HTTP/1.1 버전이고 h2는 HTTP/2.0, h3는 HTTP/3.0을 의미한다.

SPDY 프로토콜

사실 HTTP/2.0의 원조는 구글이 만든 새로운 프로토콜인 2009년 중반에 발표된 SPDY(스피디)이다.

HTTP/1.1의 메시지 포맷은 구현의 단순성과 접근성에 중점을 두고 최적화된 프로토콜이다 보니, 성능을 어느 정도 희생시킬 수 밖에 없었다.

그래서 더 효율적이고 빠른 HTTP 통신이 필요했고, 이러한 요구 사항에 의해 만들어진 것이 구글의 SPDY 프로토콜이다.

image_5

SPDY는 HTTP를 대체하는 프로토콜이 아닌 HTTP를 통한 전송을 재정의하는 형태로 구현되었다.

그래서 전송 계층의 구현만 변경하면 기존 HTTP 서버 프로그램을 그대로 SPDY에서 사용할 수 있었다.

image_6

혁신적인 성능 향상에 힘입어 SPDY를 사용하는 사이트가 늘어나게 되었고, 이러한 상황을 주시하고 있던 HTTP-WG(HTTP Working Group)는 HTTP/2.0 표준을 선보이려는 노력을 했고, 이 프로토콜의 초안으로 SPDY 프로토콜을 채택했다.

이렇게 2012년부터 2015년까지, 3년간의 노력으로 HTTP/2.0 표준이 발행된다.

그리고 몇 년간 함께 발전해오던 SPDY는 지원을 중단하였고, 그 자리를 HTTP/2.0이 대체하게 되었다.

HTTP/2.0의 개선점

Binary Framing Layer

image_7

HTTP/1.1과 HTTP/2.0의 주요한 차이점은 HTTP 메시지가 1.1 버전에서는 text로 전송되었던 것과 달리, 2.0 버전에서는 binary frame으로 인코딩되어 전송된다는 점이다.

기존 text 방식으로 HTTP 메시지를 보내는 방식의 경우, 본문은 압축되지만 헤더는 압축이 되지 않으며 헤더 중복값이 있다는 문제로 인해 HTTP/2.0에서는 바이너리로 변경된 것이다.

또한 HTTP 헤더의 경우, 헤더와 바디를 \r이나 \n과 같은 개행 문자로 구분하지만, HTTP/2.0 버전부터는 헤더와 바디를 Layer로 구분한다.

이로 인해 데이터 파싱 및 전송 속도가 증가하였고 오류 발생 가능성이 줄어들었다.

Stream과 Frame 단위

HTTP/1.1에서는 HTTP 요청과 응답이 통째로 텍스트 Message 단위로 구성되어 있었다.

HTTP/2.0 버전으로 오면서 Message라는 단위 외에 Frame, Stream이라는 단위가 추가되었다.

  • Frame
    HTTP/2.0에서는 통신의 최소 단위이며, Header 혹은 Data가 들어있다.
  • Message
    HTTP/1.1과 마찬가지로 요청 혹은 응답의 단위이며, 다수의 Frame으로 이루어진 배열 라인이다.
  • Stream
    연결된 Connection 내에서 양방향으로 Message를 주고 받는 하나의 흐름을 말한다.

image_8 Connection [ Stream { Message ( Frame ) } ]

즉, HTTP/2.0는 HTTP 요청을 여러 개의 Frame들로 나누고, 이 Frame들이 모여 요청/응답 Message가 되고, 그리고 Message는 특정 Stream에 속하게 되고, 여러 개의 Stream은 하나의 Connection에 속하게 되는 구조이다.

Multiplexing

image_9

위의 Connection [ Stream { Message ( Frame ) } ] 이미지에서 볼 수 있듯이, HTTP 헤더 메시지를 바이너리 형태의 프레임으로 나누고 하나의 커넥션으로 동시에 여러 개의 메시지 스트림을 응답 순서에 상관없이 주고 받는 것멀티플렉싱(Multiplexing)이라고 한다.

  • HTTP/1.1의 Connection Keep-Alive, Pipelining, Head of Line Blocking을 개선했다.
  • Latency만 줄여주는 것 뿐만 아닌, 결과적으로는 네트워크를 효율적으로 사용할 수 있게 하고 네트워크 비용을 줄여준다.

    특히 클라우드 시스템을 이용한다면 비용과 직결된다.

HTTP/1.1 통신 과정

HTTP/1.1 버전에서는 한 TCP 커넥션을 통해 요청을 보냈을 때, 그에 대한 응답이 도착하고 나서야 같은 TCP 커넥션으로 다시 요청을 보낼 수 있다.

따라서 웹 브라우저들은 회전 지연을 줄이기 위해 여러 개의 TCP 커넥션을 만들어 동시에 여러 개의 요청을 보내는 방법을 사용했다.

그렇다고 TCP 커넥션을 무한정으로 만들 수는 없는 노릇이기 떄문에 한 페이지에 보내야 할 요청이 수십, 수백개에 달하는 요즈음 상황에는 한계가 있었다.

통신 과정 살펴보기

image_10

  1. Request 1을 전송받기 위해서 하나의 TCP Connection 1을 열고 요청/응답한다.
  2. 다음으로 Request 2, 3, 4를 요청하는데 빠르게 전송받기 위해서 여러 개의 커넥션(TCP Connection 2, 3)을 만들어 요청/응답한다.

커넥션을 무한정으로 만들 수 없기 때문에 이러한 방식은 한계가 존재한다.

HTTP/2.0 통신 과정

반면, HTTP/2.0 버전에서는 하나의 커넥션에 여러 개의 스트림이 동시에 요청/응답한다.

HTTP/1.1은 요청과 응답이 메시지라는 단위로 구분되어 있었지만, HTTP/2.0부터는 Stream을 통해 요청과 응답이 묶일 수 있어서 여러 개의 요청을 병렬적으로 처리가 가능해졌다.

따라서 응답 프레임들은 요청 순서에 상관없이 먼저 완료된 순서대로 클라이언트에게 전달이 가능하다.

통신 과정 살펴보기

image_11

  1. Request 1을 전송받기 위해, 우선 Framing Layer를 통해 바이너리 프레임 단위로 쪼개고 하나의 TCP Connection을 만들어 통신한다.
  2. 다음으로 Request 2, 3, 4를 요청할 때는 기존의 커넷견을 이용하며, 쪼개진 프레임들은 메시지 통로를 통해 동시다발적으로 요청/응답받는다.

커넥션 낭비도 없고 병렬적으로 자원을 전송받기 때문에 매우 빠르다.

Server Push

image_12

HTTP/2.0 버전에서는 클라이언트의 요청에 대해 미래에 필요할 것 같은 리소스를 미리 보낼 수 있다.

예를 들어 클라이언트로부터 HTML 문서를 요청하는 하나의 HTTP 메시지를 받은 서버는 그 HTML 문서가 링크하여 사용하고 있는 이미지, CSS 파일, JS 파일 등의 리소스를 스스로 파악하여 클라이언트에게 미리 Push해서 브라우저의 캐시에 가져다 놓는다.

즉, 서버는 요청하지도 않은 리소소를 미리 보내 가까운 미래에 특정 개체가 필요할 때 바로 사용할 수 있도록 하여, 성능 향상을 이끌어 내는 것이다.

그래서 클라이언트가 HTML 문서를 파싱해서 필요한 리소스를 다시 요청하여 발생하게 되는 트래픽과 회전 지연을 줄여준다는 장점이 있다.

HTTP/2.0 + Push 통신 과정

image_13

  1. 서버가 클라이언트로부터 Request 1을 전송받으면, index.html에 있는 자원들을 파싱한다.
  2. 클라이언트가 따로 요청하지 않아도 서버가 알아서 미리 자원들을 클라이언트에 보낸다.

따라서 총 로드 시간이 줄어드는 이점이 있다.

Stream Prioritization

HTTP/1.1 버전에서 파이프라이닝이라는 혁신적(일 뻔한)인 기술이 있었지만, 우선순위 문제로 인한 HOL(Head of Line) Blocking 현상이 발생하게 되었고, 그로 인해 사장된 기술이 되었다.

위의 내용에 대한 자세한 설명은 #HTTP/1.1의 문제점 - HOL(Head of Line) Blocking 부분을 참고

HTTP/2.0 버전에서는 리소스 간의 의존 관계(우선순위)를 설정하여 이런 문제를 해결하였다.

Multiplexing(👆)에서 봤듯, HTTP 메시지가 개별 바이너리 프레임으로 분할되고, 여러 프레임을 멀티플렉싱 할 수 있게 되면서 요청과 응답이 동시에 이루어져 비약적으로 속도가 향상되었다.

하지만, 하나의 연결에 여러 요청과 응답이 뒤섞이면서 패킷 순서가 엉망진창이 되었다.

따라서 스트림들의 우선순위를 지정할 필요가 생겼는데, 클라이언트는 우선순위 지정 트리를 사용하여 스트림에 식별자를 설정하면서 해결하였다.

image_14 우선순위 지정 트리

  • 각각의 스트림은 1 ~ 256까지의 가중치를 갖는다.
  • 하나의 스트림은 다른 스트림에게 명확한 의존성을 갖는다.

스트림 우선순위 통신 과정

image_15

  1. 클라이언트는 서버에게 스트림을 보낼 때, 각 요청 자원에 가중치 우선순위를 지정하고 보낸다.
  2. 그렇게 요청받은 서버는 우선순위가 높은 응답이 클라이언트에 우선적으로 전달될 수 있도록 대역폭을 설정한다.
  3. 응답받은 각 프레임에는 이것이 어떤 스트림인지에 대한 고유한 식별자가 있어, 클라이언트는 여러 개의 스트림을 Interleaving1 과정으로 서로 끼워 놓은 식으로 조립한다.

최신 브라우저들은 자원의 종류, 페이지가 로드된 위치 그리고 이전 페이지 방문에서 학습한 결과에 따라 자원 요청의 우선순위를 결정하기도 한다.

HTTP Header Data Compression

HTTP/1.1 버전에서의 헤더는 아무런 압축 없이 그대로 전송되었다.

이를 개선하기 위해 HTTP/2.0 버전에서는 HTTP 메시지의 헤더를 압축하여 전송한다.

또한 HTTP/1.1에서는 연속적으로 요청되는 HTTP 메시지들에게서 헤더값이 중복되는 부분이 많아서 역시 메모리가 낭비되었다.

그러나 HTTP/2.0에서는 이전 메시지의 헤더의 내용 중, 중복되는 필드를 재전송하지 않도록하여 데이터를 절약할 수 있게 되었다.

image_16

만일 메시지 헤더에 중복값이 존재하는 경우, 위의 그림에서 Static / Dynamic Header Table 개념을 사용하여 중복 헤더를 검출하고, 중복된 헤더는 index값만 전송하고 중복되지 않은 Header 정보의 값은 허프만 코딩(Huffman coding)2 기법을 사용하는 HPACK 압축 방식으로 인코딩 처리를 통해 전송하여 데이터 전송 효율을 높였다고 보면 된다.

HTTP/2.0의 문제점

여전한 RTT (Round Trip Time)

아무리 혁신적으로 개선되었다고는 하지만, HTTP/1.1이나 HTTP/2.0은 여전히 #TCP를 이용하기 때문에 Handshake의 RTT(Round Trip Time)로 인한 지연 시간(Latency)이 발생한다.

결국, 아무리 성능 개선을 한다고 해도 절대로 고칠 수 없는 원초적인 문제점인 TCP 통신인 것이다.

TCP 자체의 HOL(Head of line) Blocking

분명 HTTP/2.0에서 HTTP/1.1의 파이프라이닝 HOL Blocking 문제를 멀티플렉싱(Multiplexing)을 통해 해결했다고 앞서 설명했지만, 기본적으로 TCP는 패킷이 유실되거나 오류가 있을 떄 재전송을 하는데, 이 재전송 과정에서 패킷의 지연이 발생하면 결국 HOL Blocking 문제는 여전히 발생하게 된다.

image_19

위의 이미지에서 볼 수 있듯이, TCP/IP 4계층에서 애플리케이션 계층(L4)에서 HTTP HOL Blocking 문제를 해결했다고 하더라도, 전송 계층(L3)에서의 TCP HOL Blocking 문제를 해결한 것은 아니기 때문이다.

image_17

중개자 캡슐화 공격

HTTP/2.0은 앞서 설명했듯이 헤더 필드의 이름과 값을 바이너리로 인코딩한다.

이를 다르게 말하면 HTTP/2.0이 헤더 필드로 어떤 문자열이든 사용할 수 있게 해준다는 뜻이다.

그래서 이를 악용하면 HTTP/2.0 메시지를 중간의 Proxy 서버가 HTTP/1.1 메시지로 변환할 때 메시지를 불법 위조를 할 수 있다는 위험성이 존재한다.

반대로 HTTP/1.1 메시지를 HTTP/2.0 메시지로 번역하는 과정에서는 다행이도 이런 문제가 발생하지 않는다.

오랜 커넥션 유지로 인한 개인 정보 누출 우려

HTTP/2.0은 기본적으로 성능을 위해 클라이언트와 서버 사이의 커넥션을 오래 유지하는 것을 염두에 두고 있다.

하지만 이로 인해 개인 정보의 유출에 악용될 가능성이 있다.

이는 HTTP/1.1의 Keep-Alive도 가지고 있는 문제이기도 하다.

UDP를 채택한 HTTP/3.0

위의 HTTP/2.0의 문제점, 더 근본적으로 지금까지의 HTTP 통신의 문제점을 한마디로 요약하자면 TCP가 문제이다.

HTTP 통신 자체가 지금까지는 TCP 기반 위에서 동작했으니 어쩔 수 없는 부분으로 문제점이라기 보다는 한계점이라고 말하는게 조금 더 맞다고 생각한다.

최근에 나온 HTTP/3.0 버전은 TCP를 버리고 UDP를 채택했다.

정확히는 UDP를 개조해서 만들어진 QUIC라는 프로토콜을 채택한 것이다.

기존 TCP는 클라이언트와 서버 간의 세션을 설정하기 위해 핸드쉐이크가 필요하며, 인증서인 TLS 마저도 세션이 보호되도록 자체 핸드쉐이크가 필요하다.

하지만 QUIC는 보안 세션을 설정하기 위해 단 한 번의 핸드쉐이크만 필요하다.

image_18

위의 이미지에서 볼 수 있듯이, 한 번 통신하는데 드는 시간의 세로축 차이가 꽤나 난다.

HTTP/2.0의 다음 버전인 HTTP/3.0에 대한 자세한 설명은 #HTTP (1-3) - TCP 대신 UDP를 채택한 HTTP/3.0 게시글을 참고

참고 사이트

드프 DrawingProcess - [Network] HTTP 버전 별 특징: HTTP v0.9 v1.0 v1.1 v2 v3

Semantics - HTTP (1) - version 별 특징 (0.9 / 1.0 / 2.0 / 3.0)

minu.log - HTTP/1.0, HTTP/1.1, HTTP/2.0, HTTP/3.0, and QUIC

Inpa Dev - 🌐 HTTP 2.0 소개 & 통신 기술 알아보기


  1. 인터리빙(Interleaving)의 사전적인 의미는 “끼워 넣기”로, IP 네트워크 즉, 유선 통신 네트워크 또는 무선 통신 구간을 통해 트래픽을 전송할 떄, 혹은 발생할 수 있는, 군집 에러를 랜덤 에러로 변환하여 에러 정정을 용이하게 하기 위해 사용되는 기법이다. ↩︎

  2. 전산학과 정보이론에서 허프먼 코딩(Huffman coding)는 무손실 압축에 쓰이는 엔트로피 부호화의 일종으로, 데이터 문자의 등장 빈도에 따라서 다른 길이의 부호를 사용하는 알고리즘이며, 1952년 당시 박사과정 학생이던 데이비드 허프먼이 《A Method for the Construction of Minimum-Redundancy Codes》란 제목의 논문으로 처음 발표했다. ↩︎

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.