2021. 11. 12. 17:12ㆍBACKEND
HTTP?
HTTP는 인터넷을 사용해봤다면 누구나 한 번쯤 들어 봤을텐데요. HyperText Transfer Protocol의 약자로, HyperText(링크를 통해 다른 문서로 연결될 수 있는 문서)를 Transfer(전송하는) Protocol(규격이 정해진 규칙 체계)입니다. 다르게 표현하자면, 웹에서 클라이언트(브라우저)가 웹 서버(httpd, nginx, apache 등)정보를 주고받을 수 있는 프로토콜입니다.
주로 글 위주로 이미지가 포함된 예전의 웹 페이지와는 달리, 이미지의 용량은 매우 커졌고 동영상의 수요와 공급이 급격히 증가하면서 기존의 통신 프로토콜을 변경해야할 필요성을 느끼게 됩니다. 변경 내역을 확인해보면서, HTTP3까지 어떤 것이 변경되고 왜 그렇게 변경되었는지 확인해보는 것이 이 포스팅의 목표입니다.
HTTP/0.9
HTTP/0.9는 One-Line Protocol로 불리며, 이 초기 HTTP는 버전 번호가 없는 상태로 발표되었습니다. HTTP는 요청이 단일 라인으로 구성되며 GET 메서드만이 존재했습니다. 서버에 연결되면 프로토콜, 서버, 포트는 불필요해지기 때문에 아래와 같이 아주 단순한 형태를 띄었습니다.
이 때는 Header도 없었는데, HTML만이 전송될 수 있었음을 의미합니다. 또, 요청에 대한 상태나 오류 코드도 없었습니다.
HTTP/1.0
HTTP1.0을 일단 해보자는 'try-and-see' 방법으로 시작됐습니다. 서버와 브러우저에 기능을 추가했고, 상호운용성 문제가 일반적이었습니다. 1996년에 이러한 문제를 해결하고자 일반적인 관례들을 적은 문서를 발행했고, 이것을 RFC 1945 라고 하며 HTTP/1.0로 정의했습니다. HTTP/1.0의 특징은 아래와 같습니다.
✔️ 버전 정보를 명시하기 시작
✔️ 응답에 status code 추가
👉🏻 브라우저가 요청에 대한 성공과 실패를 구별
✔️ 요청과 응답에 헤더 개념 추가
👉🏻 메타데이터 전송 허용,
👉🏻 유연성 및 확장성
👉🏻 Content-Type 을 명시하면서 다른 타입의 문서도 전달 가능 (ex. text/gif)
HTTP/1.1
The Standard Protocol
HTTP/1.1는 HTTP/1.0이 나온지 몇 달 안돼서 1997년 초에 공개되었습니다. HTTP/1.0에서의 모호함을 없애고 명확한 정의를 내리게 됩니다.
✔️커넥션의 재사용 가능
👉🏻 Keep-Alive header를 통해 기존 연결과의 handshake 생략가능
✔️Pipelining 추가
👉🏻 이전 요청의 응답을 완전히 전송되기 전에 다음 요청을 가능하게 해서 통신 대기 시간을 낮춤
✔️청크된 응답 지원
✔️추가적인 캐시 제어 메커니즘 도입
✔️Language, Encoding, Type 등을 포함한 컨텐츠 전송
👉🏻가장 적합한 컨텐츠를 교환
✔️Header 의 "Host" Field
👉🏻동일한 IP 주소에 다른 도메인을 호스트하는 기능이 가능해짐
많은 내용들이 담기고 특히 헤더가 무거워진 형태의 메세지를 볼 수 있습니다. HTTP/1.1은 확장성이 뛰어나서 1999년부터 새로운 RFC 문서 시리즈와 HTTP/2 릴리즈를 예견하는 상황에도 15년 동안 확장되고 안정성을 유지해왔습니다. 15년 동안 변화와 확장이 일어났고, 계속해서 알아가보도록 하겠습니다.
HTTPS
TCP/IP 스택을 통해 HTTP를 전송하는 대신, 암호화된 전송 계층인 SSL을 만들었습니다. 웹에서 주소록이나 이메일, 사용자의 위치 정보나 개인정보 등에 접근하면서 점점 보안의 중요성이 대두되면서 생겨났으며, SSL은 TLS로 발전해왔습니다. 현재는 SSL이 아닌 TLS을 사용하지만, 보편적으로 SSL이라고 부르기 때문에 SSL/TLS을 혼용하여 부른다고 합니다.
RESTful API
2000년에 HTTP 사용에 대한 새로운 사용 패턴으로 REST가 등장했습니다. 표준 규격이 없다는 단점이 있지만, 2010년 부터는 매우 일반적으로 사용되었습니다.
HTTP/1.1 동작 원리
왼쪽은 HTTP/1.1 Baseline 통신 규약이고, 오른쪽이 바로 Pipelining 기능이 도입된 HTTP/1.1 통신 과정입니다.
HTTP/1.1의 동작은 기본적으로 Connection 한 개당 하나의 요청을 처리하도록 설계되어 있습니다. 동시 전송이 불가능하고 요청과 응답이 순차적으로 이뤄집니다. 그래서 이미지 세 개를 요청한다고 하면 아래와 같습니다.
| --- a.png --- |
| --- b.png --- |
| --- c.png --- |
또, HTML 문서 안에 포함된 다수의 리소스 (Images, CSS, Script)를 처리하려면 요청할 리소스 개수에 비례해서 Latency(대기 시간)는 길어지게 됩니다.
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의 동작원리에서 일반적으로 Connection 하나에 요청 한 개를 처리한다고 했는데요. 매번 요청 별로 Connection을 만들고 TCP 상에서 동작하는 HTTP의 특성상 3-Way Handshake가 반복적으로 일어나며, 불필요한 RTT증가와 네트워크 지연을 초래하여 성능을 지연시킬 수 있습니다.
참고로 RTT란, 요청(SYN)을 보낼 때부터 요청에 대한 응답(SYN+ACK)을 받을 때까지의 왕복 시간을 의미합니다.
✏️ 무거운 Header 구조
HTTP/1.1는 여러 발전을 해오면서 헤더에 많은 메타 정보들을 저장하게 되었습니다. 사용자가 방문한 웹페이지는 다수의 http요청이 발생하게 되는데, 이 경우 매 요청시 마다 중복된 헤더값을 전송하게 됩니다. 또한 해당 domain에 설정된 cookie 정보도 매 요청시 마다 헤더에 포함되어 전송되어 성능을 저하시킵니다.
SPDY
웹 환경이 계속해서 바뀌면서 (리소스 증가, 다수의 도메인, 동적 웹 서비스, 보안의 중요성 대두 등) 구글은 전송 지연latency 문제의 해결을 집중하며 HTTP를 고속화한 새로운 프로토콜인 SPDY를 구현했습니다. SPDY는 'speedy'라는 단어를 기반으로 Google이 만든 조어로, Google이 자신들의 'Make the Web Faster' 노력의 하나로 제안한 새로운 프로토콜입니다.
SPDY는 실제로 HTTP/1.1에 비해 상당한 성능 향상과 효율성을 보여줬고 이는 HTTP/2 초안의 참고 규격이 되었습니다.
SPDY의 특징을 정리하면 아래와 같은데, 개념적인 설명은 HTTP/2와 겹치는 부분이 많으니 HTTP/2에서 더 자세하게 설명하도록 하겠습니다.
✔️ 항상 TLS 위에서 동작
👉🏻 HTTPS로 작성된 웹 사이트만 적용 가능
✔️ HTTP 헤더 압축
👉🏻 요청이 많아질 수록 압축률은 커지고, 대역폭이 작은 모바일 환경에서 효과가 크게 보임
✔️ 텍스트가 아닌 바이너리 프로토콜
👉🏻 파싱이 더 빠르고, 오류 발생 가능성이 낮음
✔️ Multiplexing
👉🏻 하나의 커넥션 안에서 다수의 독립적인 스트림을 동시에 처리
✔️ Full-duplex interleaving & Prioritization
👉🏻 다른 스트림이 끼어드는(interleaving) 것을 허용
✔️ Server Push
결국, SPDY는 HTTP의 데이터 전송 포맷과 커넥션 관리 부분을 고쳐서 TCP 커넥션을 보다 효율적으로 쓰도록 만든 것이라고 볼 수 있습니다.
다시 한 번 언급하지만, SPDY은 HTTP/2의 참고 규격이 되었습니다. 그만큼 비슷한 구조가 많은데요. 위의 특징들의 대부분이 HTTP/2에서도 보이고 있습니다.
HTTP/2
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.
https://http2.github.io/
HTTP2의 Github를 확인해 보면, HTTP/2는 HTTP/1 의 확장으로 기존의 HTTP/1과의 호환성을 유지하며 성능에 초점을 맞춘 프로토콜임을 알 수 있습니다. 그렇다면 HTTP/2의 특징에는 어떤 것들이 있는지 확인해보도록 하겠습니다.
Multiplexed Streams
HTTP/2는 하나의 TCP 연결을 통해 여러 데이터 요청을 병렬로 전송할 수 있습니다.
HTTP/2는 Multiplexed Streams를 이용하여 Connection 한 개로 동시에 여러 개의 메시지를 주고 받을 수 있으며 응답은 순서에 상관없이 Stream으로 주고 받습니다. RTT 시간이 줄어들어 별도의 최적화 과정이나 도메인 샤딩없이 웹 사이트 로드 속도가 빨라집니다. HTTP/1.1의 Connection Keep-Alive, Pipelining의 개선된 것을 알 수 있습니다.
Header Compression
HTTP/2는 중복 헤더 프레임을 압축해서 전송합니다. HPACK 규격을 사용하는데요. 클라이언트와 서버에서 모두 이전 요청에 사용된 헤더 목록을 유지관리합니다. HPACK은 서버로 전송되기 전에 각 헤더의 개별 값을 압축한 다음 이전에 전송된 헤더 값 목록에서 인코딩된 정보를 조회하여 전체 헤더 정보를 재구성합니다.
모바일과 같이 업로드 대역폭이 상대적으로 작은 경우에는 이런 HTTP 헤더 압축 방법이 특히 유용한데, 오늘날의 HTTP 헤더는 평균 2KB 가량이고, 점점 더 커지는 추세이기 때문에 HTTP 헤더 압축의 가치는 앞으로는 더 커질 것이라고 합니다.
Binary protocol
텍스트 프로토콜에서 바이너리 프로토콜로 변화했습니다. 기존 HTTP/1에서 사용된 frame의 복잡성을 편리하게 해주고, 텍스트와 공백들이 섞여 혼동이 발생하던 명령들보다 명령어를 단순하게 구현할 수 있습니다. HTTP/2 구현을 사용하는 브라우저는 네트워크를 통해 전송하기 전에 동일한 텍스트 명령을 바이너리로 변환합니다.
바이너리 프로토콜을 사용하면 어떤 점이 좋을까요?
✔️ 데이터의 파싱이 더 빠르고, 오류 발생 가능성이 낮음
✔️ 네트워크 리소스의 효과적 사용
👉🏻네트워크 지연 시간을 줄이고 처리량을 개선
✔️ 텍스트 특성과 관련된 보안 문제를 해결할 수 있음 (EX. Response Splitting Attacks )
✔️ HTTP/2의 다른 기능을 활성화
👉🏻압축, 멀티플렉싱, 우선 순위 지정, 흐름 제어 및 TLS의 효과적인 처리 등
Server Push
서버는 요청되지 않았지만 향후 요청에서 예상되는 추가 정보를 클라이언트에 전송할 수 있습니다. 예를 들어, 클라이언트가 리소스 X에 대해 요청하고 리소스 Y가 요청된 X 파일에서 참조되는 경우, 서버는 클라이언트 요청을 기다리는 대신 X와 함께 Y를 푸시하도록 선택할 수 있습니다.
Server Push는 어떤 점에서 유용하게 사용할 수 있을까요?
✔️ 클라이언트는 푸시된 리소스를 캐시에 저장
👉🏻 캐시된 리소스를 여러 페이지에 걸쳐 재사용할 수 있음
✔️ 서버는 멀티플렉싱으로 요청한 정보와 함께 푸시된 리소스를 전송가능
✔️ 서버는 푸시되는 리소스의 우선 순위를 지정할 수 있음
✔️ 클라이언트의 선택적 리소스 관리
👉🏻푸시된 리소스를 거부하거나 서버 푸시를 비활성화할 수 있음
✔️클라이언트가 멀티플렉싱되는 푸시 스트림 수 제한 가능
Stream Prioritization
클라이언트가 선호하는 응답 수신 방식을 지정해서 응답을 받을 수 있습니다. 문서 내에 CSS 파일 1개와 이미지 파일 2개가 존재하고 이를 클라이언트가 요청한다고 가정해봅시다. 이미지 파일보다 CSS 파일의 수신이 늦어진다면 브라우저 렌더링에 문제가 생기게 될 수 있는데, HTTP/2에서는 이러한 상황을 고려하여 리소스 간의 의존관계에 따른 우선순위를 설정하여 리소스 로드 문제를 해결할 수 있습니다.
참고 문서
'BACKEND' 카테고리의 다른 글
RUVVE, Backend.log (0) | 2021.12.09 |
---|---|
RUVVE, Service.log (0) | 2021.12.09 |
HTTPS, TLS/SSL 어렵지 않게 등록하기 (0) | 2021.04.25 |
Cookie Parking SERVER.log - 2 (3) | 2021.02.21 |
Cookie Parking, SERVER.log (0) | 2021.02.14 |
Backend Software Engineer
𝐒𝐮𝐧 · 𝙂𝙮𝙚𝙤𝙣𝙜𝙨𝙪𝙣 𝙋𝙖𝙧𝙠