HTTP는 1989년에 만들어진 웹의 통신 표준입니다.
97년도에 HTTP/1.1 이 릴리즈 되면서 프로토콜이 거의 수정된 적이 없었습니다.
그러나,
2015년에는 HTTP2 가 나오면서 사용되기 시작했습니다.
HTTP2 는 모바일 플랫폼, 서버 집약적인 그래픽과 비디오를 다룰 때, 레이턴시를 줄일 수 있는 여러가지 방법을 제공 했습니다.
또한 그 이후에 점점 더 인기가 많아지면서 현재에는 모든 웹사이트의 1/3 정도 이상이 HTTP2를 지원하는 것으로 알려져 있습니다.
이 설명에선 HTTP 설명을 하기보단, ver1 과 ver2 의 차이점을 위주로 설명할 예정입니다.
HTTP 가 무엇인 지를 보고 싶다면, 아래 제가 정리해놓은 글을 한 번 읽고 오시며 좋습니다.
https://devnuts.tistory.com/71?category=1003683
HTTP 란?
HTTP(HyperText Transfer Protocol)는 인터넷에서 데이터를 주고 받을 수 있는 프로토콜입니다. 이번 주제를 알아보기 전에, 프로토콜에 대해서 먼저 설명하도록 하겠습니다. 프로토콜 공통 데이터 교환
devnuts.tistory.com
[ HTTP/1.1 vs HTTP/2 ]
HTTP/1.1
1989년에 Timothy Berners-Lee 가 WWW 통신 표준으로 개발한 HTTP는 클라이언트 컴퓨터와 로컬 또는 원격 웹 서버간에 정보를 교환하는 최상위 애플리케이션 프로토콜입니다.
클라이언트에서 GET, POST 와 같은 메소드를 호출하여, 텍스트 기반 요청을 보내게 됩니다.
이에 응답으로 서버는 HTML 페이지나 리소스를 클라이언트에 보냅니다.
GET /index.html HTTP/1.1
Host: www.example.com
위 메세지 요청은 request 요청 방식입니다.
GET 메소드를 이용하여, Host 뒤에 있는 url에 잇는 서버에 데이터를 요청하게 됩니다.
이 요청에 대한 응답으로 www.example.com 서버는 Img, style.sheet, css, 등등 기타 HTML 의 리소스 등과 함께 HTML 페이지에 리턴을 하게 됩니다.
위에서 언급한 리소스는 첫 번째 데이터 호출 시에 모든 리소스가 클라이언트로 반환되는 것은 아닙니다.
웹 브라우저가 화면의 HTML 페이지를 렌더링하는데, 필요한 모든 데이터를 받을 때까지 요청과 응답이 서버와 클라이언트를 반복하게 됩니다.
HTTP/2
압축, 멀티플렉싱, 우선순위 지정 등의 기술을 사용하여, 웹 페이지 로드 레이턴시를 줄이려는 목적으로 구글에서 개발한 SPDY 프로토콜로 시작된 기술입니다.
처음부터 많은 모던 브라우저들이 표준화 작업을 지원했습니다. 이러한 지원 덕분에 많은 인터넷 사이트들은 2015년 이후에 해당 프로토콜을 많이 사용하게 됐습니다.
기술적인 관점에서, HTTP/2 와 HTTP/1.1 을 구별하는 가장 중요한 기능 중하나는 인터켓 프로토콜 스택에서 애플리케이션 계층의 일부로 간주되는 Bianary Framing Layer입니다.
모든 요청과 응답을 일반 텍스트 형식으로 관리하는 HTTP/1.1 과는 다르게, HTTP/2 는 모든 메시지를 이진 형식으로 캡슐화 하는 동시에 verb, 메서드, 헤더 등의 HTTP 문법을 유지합니다.
애플리케이션 Layer api 는 여전히 전통적인 HTTP 형식으로 메시지를 만들지만,
그 하단 레이어는 이러한 메시지를 이진수로 변환하게 됩니다.
이렇게 하면, HTTP/2 이전에 작성된 웹 애플리케이션이 새 프로토콜과 상호작용 할 때, 정상적인 작동을 할 수 있습니다.
메시지를 이렇게 이진수로 변환하면, HTTP/2 가 HTTP/1.1 에서는 사용할 수 없는 데이터 전송에 대해 새로운 접근 방식을 시도할 수 있습니다.
[ Delivery Model ]
HTTP/1.1 - Pipelining and Head-of-Line Blocking
클라이언트가 HTTP GET 요청에서 받는 최초의 응답은 가끔씩 완전히 렌더링할 수 있는 페이지 형태가 아닐 수 있습니다.
따라서, 추가로 필요한 리소스를 재요청 해야하는 경우가 있습니다.
다시말하자면, 클라이언트는 페이지를 다운로드를 한 이후에야 비로소 페이지의 전체 렌더링에 추가로 리소스가 필요한 것을 알게 됩니다. 그로 인해 클라이언트는 이러한 리소스를 추가로 요청해야 합니다.
HTTP/1.0 에서는 클라이언트는 모든 새로운 요청을 위해 TCP 연결을 끊고 새로 연결을 만들어야 해서 시간과 리소스 측면에서 많은 비용이 들었습니다.
HTTP/1.1 은 영구적 연결(Persistent connection) 과 파이프라인(PipeLine)을 도입하여 이 문제를 해결하게 됩니다.
- 영구적인 연결을 사용하면 TCP 연결을 닫으라고 직접 요청하지 않는 이상 계속 연결을 열어둡니다. 이를 통해 클라이언트는 동일한 연결을 통해 여러 요청의 각각의 응답을 기다리지 않고, 전송할 수 있습니다.
하지만,
안타깝게도 이 최적화 전력에는 피할 수 없는 병목현상이 존재합니다.
동일한 대상으로 이동할 때, 여러 데이터 패킷이 동시에 통과할 수 없기 때문에, 대기열 앞에 있는 요청이 이후의 모든 요청이 차단되어 버리게 됩니다. 이를 HOL(Head of line) 블로킹으로 알려져 있습니다.
HTTP/1.1 에서 연결 효율성을 최적화 하는데 있어 중요한 문제입니다.
별도의 병렬 TCP 연결을 추가하게되면, 이러한 문제를 완화시킬 수는 있지만 클라이언트와 서버간에 동시에 연결할 수 있는 TCP 숫자는 제한이 있으며, 새로운 연결은 상당한 리소스를 필요로 합니다.
HTTP/2 - Binary framing Layer (이진 프레임 레이어)
이진 프레임 레이어는 요청과 응답을 인코딩하고, 이를 더 작은 패킷으로 잘라 데이터 전송의 유연성을 향상 시킵니다.
HTTP/1.1 에서 발생한 HOL 블로킹 이슈의 영향을 줄이기 위해 여러 개의 TCP 연결을 사용하는 HTTP/1.1 과는 다르게,
HTTP/2 는 두 컴퓨터 사이에 단일 연결 개체를 설정합니다. 이 연결에는 여러 데이터 스트림이 있습니다.
각 스트림은 요청과 응답 형식의 여러 메시지로 구성이 됩니다.
마지막으로 이러한 각 메시지는 프레임이라는 작은 단위로 분할됩니다.
가장 세분화된 레벨에서, 통신 채널은 각각 특정 스트림에 태그가 지정된 이진 인코딩 프레임 다발로 구성됩니다.
이렇게 만들어진 태그를 사용하면, 전송의 반대쪽 끝에서 다시 재조립할 수 있습니다.
인터리빙(interleaving) 된 요청과 응답은, 멀티플렉싱이라는 프로세스 뒤에서 메시지를 차단하지 않고 병렬로 실행이 가능해집니다.
멀티플렉싱은 다른 메시지가 완료될 때까지 기다릴 필요가 없도록, HTTP의 HOL 문제를 해결합니다.
이는 서버와 클라이언트가 동시에 요청과 응답을 보낼 수 있다는 것을 의미하므로,
제어 능력을 높이고 연결을 보다 효율적으로 관리할 수 있습니다.
"통신 에서 다중화(Multiplexing) 라는 용어는 두 개 이상의 저 수준의 채널들을 하나의 고 수준의 채널로 통합화는 과정을 말하며, 역다중화 (inverse multipleing, demultiplexing) 과정을 통해 원래의 채널 정보들을 추출할 수 있다. 각각의 채널들은 미리 정의된 부호화 틀(coding scheme)을 구분할 수 있다."
멀티 플렉싱은 클라이언트가 여러 스트림을 병렬적으로 구성할 수 있게 해주기 때문에, 이들 스트림은 단일 TCP 연결만 사용하면 됩니다. 출처당 하나의 영구적인 접속은 네트워크 전체의 메모리와 처리 공간을 줄임으로써 HTTP/1.1 에 성능향상을 가져올 수 있게 됩니다.
또한 네트워크 및 대역폭 활용도가 향상되어 전체 운영비 절감이 됩니다.
HTTP/2 - Streaming prioritization (스트리밍 우선순위)
동일한 리소스를 두고 경쟁이 일어나는 요청 사이의 문제를 해결할 뿐만 아니라, 개발자가 애플리케이션 성능을 더 잘 최적화 하기 위해서 요처의 상대적 가중치를 정의할 수 있도록 도와줍니다.
이진 프레임 레이어는 메시지를 병렬 데이터 스트림으로 구성하여, 클라이언트가 서버에 동시 요청을 보낼 때, 각 스트림에 1~256 사이의 가중치를 할당하여 요청 중인 응답의 우선순위를 지정할 수 있습니다.
숫자가 클수록 우선순위기 높습니다.
이외에도 클라이언트는 각 스트림의 종속성을 ID로 지정하여, 명시합니다.
상위 ID가 없다면 루트 스트림에 종속된 것으로 간주 됩니다.
아래는 우선순위를 지정한대로 스트림이 되는 그림입니다.
애플리케이션 개발자는, 필요에 따라서 요청 가중치를 설정할 수 있습니다.
예를 들어, 웹페이지에 미리보기 이미지를 먼저 제공한 뒤에, 고해상도 이미지를 로딩하는데 낮은 우선순위를 부여할 수 있습니다.
HTTP/2 는 이러한 가중치 할당 기능을 제공함으로써, 개발자가 웹페이지 렌더링을 더 잘 제어할 수 있도록 도와줍니다.
또한,
프로토콜은 클라이언트가 사용자 인터랙션에 대응하여, 런타임 시에 종속성을 변경하고 가중치를 재할당 할수 있도록합니다.
그러나, 특정 스트림의 특정 리소스 엑세스가 차단된 경우에는, 서버 자체에서 우선순위를 변경할 수도 있습니다.
[ Buffer Overflow ]
두 시스템 간 TCP 연결에서, 클라이언트와 서버 모두 아직 처리되지 않은 수신요청을 보관하는데,
사용할 수 있는 일정 크기의 버퍼 공간이 있습니다. 그러나, 이러한 버퍼가 크지 않은 상황이 있을 수 있습니다
예를들어,
서버가 제한된 버퍼 크기나 낮은 대역폭으로 인해 클라이언트 애플리케이션이 처리할 수 없는 속도로 많은 양의 데이터를 push 하고 있을 수 있습니다. 마찬가지로 클라이언트가 서버에 대용량 이미지나 비디오를 업로드 할 때, 서버 버퍼가 오버 플로우가 되어 패킷 손실이 일어날 수 있습니다.
이러한 버퍼 오버플로우를 방지하려면, 흐름제어 메커니즘은 송신자가 수신자를 데이터로 압도하는 것을 방지해야 합니다.
HTTP/1.1
흐름제어 메커니즘은 TCP 연결에 의존하게 됩니다. 연결이 시작되면, 클라이언트와 서버 모두 시스템 기본설정을 사용하여 버퍼 크기를 세팅해 둡니다. 수신자의 버퍼크기가 데이터로 채워지면, 버퍼에 남아있는 사용공간을 알려줍니다. 이러한 수신 윈도우는 ACK 패킷이라고 불리우는 시그널로 전송이 됩니다.
이 크기가 0이 되면, 클라이언트가 내부 버퍼를 지운 다음에 데이터 전송을 다시 시작할 수 있도록 요청할 때까지 송신자는 더 이상 데이터를 보내지 않습니다.
여기서 주목해야할 부분은 TCP 연결을 기반으로 하는 수신을 사용하면, 연결의 어느 한쪽 끝에만 흐름 제어를 구현할 수 있다는 것입니다.
HTTP/1.1 은 버퍼 오버플로우를 피하기 위해 전송계층에 의존하므로, 각각의 새로운 TCP 연결은 별도의 흐름제어 메커니즘이 필요합니다.
HTTP/2
단일 TCP 연결 내에서 데이터 스트림을 멀티플렉싱을 합니다. 따라서, TCP 연결 레벨의 수신으로는 개별 스트림의 전송을 규제하는 것이 어렵습니다. 그래서 HTTP/2는 클라이언트와 서버가 전송계층에 의존하는 대신에 자체적인 흐름제어를 구현하도록 허용함으로 써 이 문제를 해결합니다.
응용계층은 사용가능한 버퍼공간을 통신하여, 클라이언트와 서버 사이에 멀티플렉스 스트림 수준에서 수신 창을 설정할 수 있도록 해줍니다. WINDOW_UPDATE 프레임을 통해서, 초기 연결 후 흐름제어 컨트롤을 제어하거나 수정할 수 있습니다.
위 방법은 응용 계층 레벨에서 데이터 흐름을 제어하기 때문에, 흐름제어 메커니즘은 수신 창을 조정하기 전에 신호가 최종 목적지에 도달하는 것을 기다릴 필요가 없습니다. 중간 노드는 흐름제어 설정 정보를 참고하여 자체 리소스 할당을 결정하고, 그에 따라 수정이 가능해집니다. 이러한 방식으로 각 중간 서버는 고유한 커스텀 리소스 전략을 구현하여 연결의 효율성을 높일 수 있습니다.
이러한 흐름제어 유연성은 적절한 리소스 전략을 생성할 때 유리해질 수 있습니다.
예를 들면 클라이언트는 첫 번째 이미지를 먼저 표시하고, 더 중요한 리소스를 가져오는 동안 그 이미지를 미리 볼 수 있도록 할 수 있습니다.
클라이언트가 중요한 리소스를 가져오면, 브라우저는 이미지의 나머지 부분을 다시 가져옵니다 따라서 흐름제어 구현을 클라이언트와 서버로 미루면 웹 애플리케이션의 성능을 향상 시킬 수 있습니다.
[ Resource request forecasting ]
일반적인 웹 애플리케이션에서는, 클라이언트는 GET 요청을 보내서 HTML 페이지를 수신합니다.
index.html 의 내용을 검사하는 동안, 클라이언트는 CSS 및 Javascript file 과 같은 추가 리소스를 가지고 와야하는 걸 발견합니다
클라이언트는 이러한 초기 GET 요청으로부터 응답을 받은 후에만, 추가 리소스가 존재한다는 것을 알 수 있으므로, 이러한 리소스를 가져오고 페이지를 완성하기 위해 추가로 요청을 해야합니다. 따라서, 추가적인 요청은 어쩔 수 없이 연결 로드 시간을 증기시키게 됩니다.
그러나, 이 문제는 해결책이 존재합니다.
서버는 클라이언트가 추가 파일을 필요로 한다는 것을 미리 알 수 있기 때문에 서버가 이러한 요청을 받기 전에 리소스를 클라이언트로 전송하여 시간을 절약할 수 있습니다.
HTTP/1.1
리소스 인라이닝
이 기술은 서버가 초기 GET 에 응답하여 보내는 HTML 문서에 직접 필요한 리소스를 포함하여 보내줄 수 있습니다.
클라이언트가 페이지를 렌더링하기 위하여 CSS file이 필요한 경우, 해당 요청이 오기 전에 필요한 리소스를 제공하여 클라이언트가 보내야하는 요청 수를 줄입니다.
하지만,
이러한 리소스 인라이닝에는 몇 가지 문제점이 있습니다.
- HTML 문서에 이렇게 리소스를 포함 시키는 것은 텍스트 형식이 아닌 큰 파일이 있을 경우, HTML 문서의 크기를 증가 시켜 결국에는 연결 속도가 감소되어 이 기술로 얻는 이점을 상쇄하는 경우입니다.
- 인라인 리소스는 HTML 문서와 분리되지 않았으므로 클라이언트가 이미 가지고 있는 리소스를 거부하거나, 캐시를 쓸 수 있는 메커니즘이 없습니다. 즉, 가장 큰 단점은 리소스와 문서를 분리할 수 없다는 것입니다.
HTTP/2
서버 푸쉬
HTTP/2 는 클라이언트가 초기 GET 요청에 대해 여러 개의 동시 응답을 허용하므로, 서버는 요청한 HTML 페이지와 함께 클라이언트에 리소스를 전송하여, 클라이언트가 요청하기 전에 리소스를 제공할 수 있게끔 합니다. 이를 서버 푸쉬로가 합니다.
이러한 방식으로 HTTP/2 연결은 푸시된 리소스와 문서 간의 분리를 유지하면서, 리소스 인라인이라는 목표를 동시에 달성할 수 있습니다.
이는 클라이언트가 메인 HTMl 외에 다른 리소스를 거절할 수 있다는 것을 의미합니다.
여기에서 중요한 것은 클라이언트 제어입니다.
클라이언트가 서버 푸시 우선순위를 조정하거나, 서버 푸시를 비활성화 해야하는 경우, 언제든지 프레임을 보내 이 HTTP/2 기능을 수정할 수가 있습니다.
서버 푸쉬 기능은 많은 잠재력을 가지고 있는 것 같지만, 서버 푸시가 꼭 정답만은 아닙니다.
예를 들어 일부 웹 브루아저는 클라이언트에 이미 캐시된 리소스가 있더라도 푸시된 요청을 항상 취소할 수는 없습니다.
클라이언트가 서버에 중복된 리소스를 보낼 수 있도록 허용한 경우, 서버에 푸쉬를 해버리면 연결이 불 필요하게 낭비가 될 수 있습니다.
따라서, 서버 푸쉬는 개발자의 재량에 달려 있습니다.
[ Compression ]
웹 애플리케이션을 최적화하는 일반적인 방법은, 압축 알고리즘을 사용하여 클라이언트와 서버 간의 HTTP 메시지 크기를 줄이는 것입니다. HTTP/1.1 과 HTTP/2 에서 모두 이 전략을 사용하고 있씁니다.
HTTP/1.1
gzip 등의 기술은 CSS, Javascript file 크기를 줄이기 위해 HTTP 메시지로 전송되는 데이터를 압축하는데, 오랫동안 사용되어져 왔습니다. 그러나, 메시지의 헤더는 항상 일반 텍스트로 전송됩니다. 각 헤더는 상당히 작지만, 많은 요청이 이루어질 경우, 이 압축되지 않은 헤더의 존재는 데이터의 부담이 가중되며, 특히 많은 다른 리소스 간 요청이 필요한 복잡한 API를 사용하는 웹 애플리케이션에는 불이익이 있습니다. 또한, 쿠키글 사용하게 되면 헤더가 커지므로 어떻게든 압축을 하는 것이 필요할 수 있습니다.
HTTP/2
HTTP/2 에서 반복적으로 중요한 포인트 중하나는, 이진 프레임 레이어를 사용하여 세부 정보에 대한 제어권을 높일 수 있다는 것입니다.
헤더 압축도 마찬가지입니다.
HTTP/2 에서는 데이터에서 헤더를 분할하여, 헤더 프레임과 데이터 프레임을 생성할 수 있습니다.
그런 다음에 HTTP/2 전용 압축 프로그램 HPACK 이 헤더를 압축할 수 있습니다.
이 알고리즘은 Huffman coding 을 사용하여, 헤더 메터데이터를 인코딩할 수 있으므로, 크기를 크게 줄일 수 있습니다.
또한 HPACK은 이전에 전송된 메타데이터 필드를 추적하고, 클라이언트와 서버 간에 공유된 동적으로 변경된 인덱스에 따라서 해당 필드를 추가로 압출할 수 있습니다.
request 1
method: GET
scheme: https
host: example.com
path: /academy
accept: /image/jpeg
user-agent: Mozilla/5.0 ...
request 2
method: GET
scheme: https
host: example.com
path: /academy/images
accept: /image/jpeg
user-agent: Mozilla/5.0
위 요청에서 변화된 건 path 의 값이 다릅니다.
이 때, HPACK은 request 1 과 request 2에서 다른 path만 보내게 됩니다.
request 2
path: /academy/images
HPACK과 다른 압축방법을 사용해서, HTTP/2는 클라이언트와 서버 사이의 레이턴시를 감소시키는 기능을 제공할 수 있게 됩니다.
'CS > Network' 카테고리의 다른 글
http VS https 에 대하여 (0) | 2022.04.30 |
---|---|
HTTP 란? (1) | 2022.04.17 |