HTTP가 뭥미?
HTTP란 개발자라면 한번 쯤은 들어봤을 겁니다. HyperText Transfer Protocol의 약자로, 직역하자면 HyperText(링크를 통해 다른문서로 연결될 수 있는 문서)를 Transfer(전송하는) Protocol(규격이 정해진 규칙 체계)입니다.
쉽게 얘기해보자면 인터넷 브라우저와 서버간에 이런 저런 정보를 주고 받으면서 우리가 여러 사이트들을 이용하는 건데 여기서 브라우저와 서버사이에 이런 저런 정보를 주고받을 때 다 자기스타일대로 주거니 받거니 하면 주는 쪽과 받는 쪽이 달라질 때마다 매번 서로의 방식을 물어보고 맞춰야 하는 아주 비효율적인 일이 발생할 겁니다.
때문에 정보를 주고 받을 때 국제적으로 이런 방법으로 주고 받자~ 라고 약속을 한 것이 HTTP라고 생각하면 도움이 될 겁니다.
주로 텍스트 위주의 예전 웹 페이지와는 달리, 이미지가 포함되고 그 이미지의 용량이 점점 커지고, 동영상의 수요와 공급이 증가하면서 기존의 통신 프로토콜을 변경해야 할 필요성을 느끼게 됩니다. 이로인해 현재 HTTP3 까지 발전해왔고 이에 대해 알아봅시다.
HTTP/0.9
HTTP/0.9는 One-Line Protocol로 불리며, 초기 HTTP는 버전 번호가 없는 상태로 발표되었습니다. HTTP는 요청이 단일 라인으로 구성되며 GET 메서드만이 존재했습니다. 서버에 연결되면 프로토콜, 서버, 포트는 불필요해지기 때문에 아래와 같이 아주 단순한 형태를 띄었습니다.
//Request Message
GET /mypage.html
//Response Message
<HTML>
A very simple HTML Page
</HTML>
이 때는 Header도 없었는데, HTML만이 전송될 수 있었음을 의미합니다. 또, 요청에 대한 상태나 오류 코드도 없었습니다.
HTTP/1.0
HTTP1.0은 일단 해보자는 'try-and-see' 방법으로 시작되었습니다. 서버와 브라우저에 기능을 추가했고, 상호운용성 문제가 일반적이었습니다. 1996년에 이러한 문제를 해결하고자 일반적인 관례들을 적은 문서를 발행했고, 이것을 RFC 1945라고 하며 HTTP/1.0으로 정의했습니다.
HTTP/1.0의 특징은 아래와 같습니다.
✔️ 버전 정보를 명시하기 시작
✔️ 응답에 status code 추가
👉 브라우저가 요청에 대한 성공과 실패를 구별
✔️ 요청과 응답에 header 개념 추가
👉 metadata 전송 허용 (metadata란? https://developer.mozilla.org/en-US/docs/Glossary/Fetch_metadata_request_header , https://terms.naver.com/entry.naver?docId=1224192&cid=40942&categoryId=32840)
👉 유연성 및 확장성
👉 Content-Type을 명시하면서 다른 타입의 문서도 전달가능(ex. text, gif 등)
//Request Message
GET /mypage.html HTTP/1.0
User-Agent : NCSA_Mosaic/2.0(Window 3.1)
//Response Message
200 OK
Date : Tue, 15 Nov 1994 08:12:31 GMT
Server : CERN/3.0 libwww/2.17
Content-Type : text/html
<HTML>
A page with an image
<IMG SRC="/myimage.gif">
</HTML>
//Request Message
GET /mypage.html HTTP/1.0
User-Agent : NCSA_Mosaic/2.0(Window 3.1)
//Response Message
200 OK
Date : Tue, 15 Nov 1994 08:12:31 GMT
Server : CERN/3.0 libwww/2.17
Content-Type : text/gif
HTTP/1.1
HTTP/1.1은 HTTP/1.0이 나온지 몇 달 안돼서 1997년 초에 공개되었습니다. HTTP/1.0에서의 모호함을 없애고 명확한 정의를 내리게 됩니다.
✔️ 커넥션의 재사용 가능
👉 Keep-Alive header를 통해 기존 연결과의 handshake 생략가능
✔️ Pipelining 추가
👉 이전 요청의 응답이 완전히 전송되기 전에 다음 요청을 가능하게 해서 통신 대기 시간을 낮춤
✔️ chunk된 응답 지원
✔️ 추가적인 cache 제어 메커니즘 도입
✔️ Language, Encoding, Type 등을 포함한 컨텐츠 전송
👉 가장 적합한 컨텐츠를 교환
✔️ Header 의 "Host" field
👉 동일한 IP 주소에 다른 도메인을 호스트하는 기능이 가능해짐
//Request Message
GET /en-US/docs/Glossary/Simple_header HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US, en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/en-US/docs/Glossary/Simple_header
//Response Message
200 OK
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Wed, 20 Jul 2016 10:55:30 GMT
Etag: "547fa7e369ef56031dd3bff2ace9fc0832eb251a"
Keep-Alive: timeout=5, max=1000
Last-Modified: Tue, 19 Jul 2016 00:59:33 GMT
Server: Apache
Transfer-Encoding: chunked
Vary: Cookie, Accept-Encoding
많은 내용들이 담기고 특히 header가 무거워진 형태의 메세지를 볼 수 있습니다. HTTP/1.1은 확장성이 뛰어나서 1999년부터 새로운 RFC 문서 시리즈와 HTTP/2 release를 예견하는 상황에도 15년동안 확장되고 안정성을 유지해왔습니다. 15년 동안 변화와 확장이 일어났고, 계속해서 알아가보도록 하죠
HTTPS
TCP/IP 스택을 통해 HTTP를 전송하는 대신, 암호화된 전송 계층인 SSL(Secure Sockets Layer)을 만들었습니다. 웹에서 주소록이나 이메일, 사용자의 위치 저보나 개인정보 등에 접근하면서 점점 보안의 중요성이 대두되면서 생겨났으며, SSL은 TLS(Transport Layer Security)로 발전 해왔습니다. 혀재는 SSL이 아닌 TLS를 사용하지만, 보편적으로 SSL이라고 부르기 때문에 SSL/TLS를 혼용하여 부른다고 합니다.
RESTful API
2000년에 HTTP 사용에 대한 새로운 사용 패턴으로 REST가 등장했습니다. 표준규격이 없다는 단점이 있지만, 2010년 부터는 매우 일반적으로 사용되었습니다.
HTTP/1.1 동작 원리
왼쪽은 HTTP/1.1 Baseline 통신 규약이고, 우측은 Pipelining 기능이 도입된 통신과정입니다.
보시다시피 HTTP/1.1의 동작은 커넥션 하나를 열어 하나씩 요청을 보내야합니다. 하나의 요청이 끝나야 다음 요청을 보낼 수 있기 때문에 요청이 많을수록 비효율적입니다. 또 HTML 문서 안에 포함된 다수의 리소스(이미지, CSS, Script)를 처리하려면 요청할 리소스 개수에 비래해서 Latency가 길어지게 되죠.
하지만 파이프라이닝은 이전 요청의 응답이 끝나기 전에 다음 요청을 가능하게 해서 요청을 여러개 보낼 수 있는데 이것도 동시에 6개 정도 뿐이기 때문에 궁극적인 방법이라고는 할 수 없습니다. 때문에 요청의 수를 줄이기 위해 이미지는 스프라이트로 만들어 한 번에 받고, 걸프, 그런트 같은 번들러로 js파일이나 css파일을 하나로 합치곤 했습니다.
HTTP/1.1 단점
⚠️ HOL Blocking(Head of Line Blocking)
HTTP/1.1의 Pipelining은 한 커넥션에 순차적인 여러 요청을 연속적으로 하고, 그 순서에 맞춰 응답을 받는 방식으로 지연 시간을 줄였습니다. 하지만 순차적으로 응답을 받다보니 이전에 받은 응답이 길어지면 그 이후의 응답들까지 지연되는 문제점이 있죠.
예를 들어 3개의 이미지를 받기 위해 아래와 같이 순서대로 요청을 보냈다고 했을 때, 보낸 순서에 맞게 응답을 받아야 합니다.
| ---- a.png ---- |
| ---- b.png ---- |
| ---- c.png ---- |
a.png의 응답이 길어지면 자연스레 뒤에 있는 응답도 지연되게 되는거죠.
⚠️ RTT(Round Trip Time) 증가
HTTP/1.1의 동작원리에서 일반적으로 커넥션 하나에 요청 한 개를 처리한다고 했죠? 매번 요청 별로 커넥션을 만들고 TCP상에서 동작하는 HTTP의 특성상 HandShake가 반복적으로 일어나며, 불필요한 RTT증가와 네트워크 지연을 초래하여 성능을 지연시킬 수 있습니다.
❓RTT란, 요청을 보낼 때부터 요청에 대한 응답을 받을 때까지의 왕복 시간을 말합니다.
⚠️ 무거운 Header
HTTP/1.1은 발전을 해오면서 헤더에 많은 메타 정보들을 저장하게 되었습니다. 사용자가 방문한 웹페이지는 다수의 http요청이 발생하게 되는데, 이 경우 매 요청시 마다 중복된 헤더값을 전송하게 됩니다. 또한 해당 domain에 설정된 쿠키정보도 매 요청시 마다 헤더에 포함해서 전송되므로 성능을 저하시킵니다.
SPDY
웹 환경이 계속해서 바뀌면서(리소스 증가, 다수의 도메인, 동적 웹 서비스, 보안의 중요성 대두 등) 구글은 전송지연 문제의 해결을 집중하며 HTTP를 고속화한 새로운 프로토콜인 SPDY를 구현했습니다. SPDY는 '빠른'이라는 의미의 'speedy'라는 단어를 기반을 구글이 만든 단어로, 구글이 자신들의 'Make the Web Faster'노력의 하나로 제안한 새로운 프로토콜입니다.
SPDY는 실제로 HTTP/1.1에 비해 상당한 성능 향상과 효율성을 보여주었고 이는 HTTP/2 초안의 참고 규격이 되었습니다.
SPDY의 특징을 정리하면 아래와 같은데 개념적인 부분은 HTTP/2와 겹치는 부분이 많습니다.
✔️ 항상 TLS 위에서 동작
👉 HTTPS로 작성된 웹 사이트만 적용 가능
✔️ HTTP header 압축
👉 요청이 많아질 수록 압축률 증가, 대역폭이 작은 모바일 환경에서 효과 증대
✔️ 텍스트가 아닌 binary protocol
👉 파싱이 더 빠르고, 오류 발생 가능성이 낮음
✔️ Multiplexing
👉 하나의 connection 안에서 다수의 독립적인 stream을 동시에 처리
✔️ Full-duplex interleaving & prioritization
👉 다른 stream이 끼어드는(interleaving) 것을 허용
결국, SPDY는 HTTP의 데이터 전송 포맷과 connection 관리 부분을 고쳐서 TCP connection을 보다 효율적으로 쓰도록 만든 것이라 볼 수 있습니다.
위에서 언급했듯, SPDY는 HTTP/2의 참고 규격이 되었습니다. 때문에 비슷한 구조가 많습니다. 그래서 위 특징들의 대부분이 HTTP/2에서도 보이고 있습니다.
HTTP/2
HTTP/2 공식 github 페이지의 서문을 보면 HTTP/2의 목적을 명확히 알 수 있다.
HTTP/2 is a replacement for how HTTP is expressed “on the wire.” It is not a ground-up rewrite of the protocol; HTTP methods, status codes and semantics are the same, and it should be possible to use the same APIs as HTTP/1.x (possibly with some small additions) to represent the protocol.
The focus of the protocol is on performance; specifically, end-user perceived latency, network and server resource usage. One major goal is to allow the use of a single connection from browsers to a Web site.
The basis of the work was SPDY, but HTTP/2 has evolved to take the community’s input into account, incorporating several improvements in the process.
https://http2.github.io/
요약하자면 완전히 새로운 프로토콜이 아닌 SPDY의 개선사항을 적용해 수정된 성능향상에 초점을 맞춘 프로토콜이라고 할 수 있다.
HTTP/2의 특징들은 다음과 같습니다.
✅ Multiplexed Streams
HTTP/2는 하나의 TCP 연결을 통해 여러 데이터 요청을 병렬로 전송할 수 있습니다.
HTTP/2는 Multiplexed Streams를 이용하여 connection 한 개로 동시에 여러 개의 메세지를 주고 받을 수 있으며 응답은 순서에 상관없이 stream으로 주고 받습니다. RTT 시간이 줄어들어 별도의 최적화 과정이나 도메인 sharding 없이 웹 사이트 로드 속도가 빨라집니다. HTTP/1.1의 connection Keep-Alive, Pipelining의 개선된 점도 볼 수 있습니다.
✅ Header Compression
HTTP/2는 중복 헤더 프레임을 압축해서 전송합니다. HPACK 규격을 사용하는데, 클라이언트와 서버에서 모두 이전 요청에 사용된 헤더 목록을 유지관리합니다. HPACK은 서버로 전송되기 전에 각 헤더의 개별 값을 압축한 다음 이전에 전송된 헤더 값 목록에서 인코딩된 정보를 조회하여 전체 헤더 정보를 재구성합니다.
모바일과 같이 업로드 대역폭이 상대적으로 작은 경우에는 이런 HTTP 헤더 압축방법이 특히 유용합니다.
✅ Binary Protocol
텍스트 프로토콜에서 바이너리 프로토콜로 변화했습니다. 기존 HTTP/1에서 사용된 frame의 복잡성을 편리하게 해주고, 텍스트와 공백들이 섞여 혼동이 발생하던 명령들보다 명령어를 단순하게구현할 수 있습니다. HTTP/2구현을 사용하는 브라우저는 네트워크를 통해 전송하기 전에 동일한 텍스트 명령을 바이너리로 변환합니다.
Binary protocol의 장점
✔️ 데이터의 parsing이 더 빠르고, 오류 발생 가능성이 낮음
✔️ 네트워크 리소스의 효과적 사용
👉 네트워크 지연 시간 줄이고 처리량 개선
✔️ 텍스트 특성과 관련된 보안 문제를 해결할 수 있음
✔️ HTTP/2의 다른 기능을 활성화
👉 압축, 멀티플렉싱,우선 순위 지정, 흐름 제어 및 TLS의 효과적인 처리 등
✅ Server Push
서버는 요청되지 않았지만 향후 요청에서 예사오디는 추가 정보를 클라이언트에 전송할 수 있습니다.
예를 들어, 클라이언트가 리소스 X에 대해 요청하고 리소스 Y가 요청된 X파일에서 참조되는 경우, 서버는 클라이언트 요청을 기다리는 대신 X와 함께 Y를 푸시하도록 선택할 수 있습니다.
Server Push의 장점
✔️ 클라이언트는 푸쉬된 리소스를 캐시에 저장
👉 캐시된 리소스를 여러 페이지에 걸쳐 재사용할 수 있음
✔️ 서버는 멀티 플렉싱으로 요청한 정보와 함께 푸시된 리소스를 전송가능
✔️ 서버는 푸시되는 리소스의 우선 순위를 지정할 수 있음
✔️ 클라이언트의 선택적 리소스 관리
👉 푸시된 리소스를 거부하거나 서버 푸시를 비활성화 할 수 있음
✔️ 클라이언트가 멀티플렉싱되는 푸시 스트림 수 제한 가능
✅ Stream Prioritization
클라이언트가 선호하는 응답 수신 방식을 지정해서 응답을 받을 수 있습니다. 문서 내에 CSS 파일 1개와 이미지 파일 2개가 존재하고 이를 클라이언트가 요청한다고 해봅시다. 이미지 파일보다 CSS파일의 수신이 늦어진다면 브라우저 렌더링에 문제가 생기게 될 수 있는데, HTTP/2에서는 이러한 상황을 고려하여 리소스 간의 의존관계에 따른 우선순위를 설정하여 리소스 로드 문제를 해결할 수 있습니다.
자 그럼 단점은 없느냐
HTTP/2 단점
하지만 HTTP2는 여전히 TCP를 이용하기 때문에 Handshake의 RTT(Round Trip Time)로인한 Latency(지연시간), TCP의 HOLB 문제는 해결 할 수 없습니다.
사실 HTTP 레벨에서의 HOLB와 TCP 레벨에서의 HOLB는 다른 의미이기는 하나 결국 어떤 요청에 병목이 생겨서 전체적인 레이턴시가 늘어난다는 맥락으로 본다면 동일하다고 할 수 있습니다.
TCP를 사용한 통신에서 패킷은 무조건 정확한 순서대로 처리되어야 한다. 수신 측은 송신 측과 주고받은 시퀀스 번호를 참고하여 패킷을 재조립해야하기 때문입니다.
그래서 통신 중간에 패킷이 손실되면 완전한 데이터로 다시 조립할 수 없기 때문에 절대로 그냥 넘어가지 않죠. 무조건 송신 측은 수신 측이 패킷을 제대로 다 받았다는 것을 확인한 후, 만약 수신 측이 제대로 패킷을 받지 못했으면 해당 패킷을 다시 보내야 합니다.
또한 패킷이 처리되는 순서 또한 정해져있으므로 이전에 받은 패킷을 파싱하기 전까지는 다음 패킷을 처리할 수도 없습니다. 이렇게 패킷이 중간에 유실되거나 수신 측의 패킷 파싱 속도가 느리다면 통신에 병목이 발생하게 되는 현상을 HOLB라고 부릅니다. 이건 TCP 자체의 문제이므로 HTTP/1 뿐만 아니라 HTTP/2도 가지고 있는 문제입니다.
HTTP/3
가장 큰 특징은 TCP가 아닌 UDP를 사용한다는 것입니다.
정확히 말하면 HTTP3는 QUIC이라는 프로토콜 위에서 돌아가는 HTTP인데,
여기서 QUIC이란
Quick UDP Internet Connection의 약자로 UDP를 기반으로 TCP + TLS + HTTP의 기능을 모두 구현하는 프로토콜입니다.
구글에서 개발했던 SPDY 기술이 HTTP/2의 기반 기술이었는데 역시 구글에서 개발한 QUIC이 HTTP/3의 기반 기술이 되었습니다.
구글이 QUIC을 만들 때 UDP를 선택한 이유에는 기존의 TCP를 수정하기가 어려운데다가, 백지 상태나 다름없는 UDP를 사용함으로써 QUIC의 기능을 확장하기 쉬웠기 때문이라고 한다.
때문에 QUIC을 사용하므로써 나오는 HTTP/3의 장점은 다음과 같다.
HTTP/3 특징 및 장점
✔️ RTT감소로 인한 지연시간(Latency) 단축
👉 QUIC은 TCP 사용하지 않기에 3 Way Handshake과정 불필요 => 기존 TCP 연결 +TLS 까지 = 3RTT 필요
👉 QUIC은 첫 연결설정에 1RTT만 필요 => 연결 설정에 필요한 정보와 함께 데이터도 보내버리기 때문
👉 그리고 한번 연결에 성공했다면 서버는 그 설정을 캐싱해놓고 있다가,다음 연결때는 캐싱해놓은 설정을 사용하여 바로 연결을 성립시키기 때문에 0RTT 필요
✔️ 패킷 손실 감지에 걸리는 시간 단축
👉 QUIC은 header에 별도의 패킷 번호 공간을 부여해 패킷 고유의 번호를 가지고 있는다.
✔️ Multiplexing 지원
👉 HTTP/2와 동일하게 지원
✔️ 클라이언트의 IP가 바뀌어도 연결이 유지됨
👉 TCP의 경우 소스의 IP주소와 포트, 연결 대상의 IP 주소와 포트로 연결을 식별하기에 클라이언트의 IP가 바뀌는 상황이 발생하면 연결이 끊어짐 => 다시 연결위해 handshake 거쳐야 하고, 이 과정에서 다시 latency가 발생
👉 반면 QUIC은 connection ID를 사용하여 서버와 연결을 생성한다. connection ID는 랜덤한 값일 뿐, 클라이언트의 IP와는 전혀 무관한 데이터이기 때문에 클라이언트의 IP가 변경되더라도 기존의 연결을 계속유지할 수 있다.
참고문서