HTTP/1.x와 HTTP/2 알아보기
현재 개발중인 HTTP/3를 알아보기에 앞서 HTTP/1.x와 HTTP/2에 대해서 알아보자.
이 글을 읽고나서 HTTP/3와 QUIC 알아보기를 읽으면 더 흥미로울 것이다.
1. HTTP/0.9
HTTP 초기 버전에는 버전 번호가 없었다. HTTP/0.9 이후에 차후 버전과 구별하기 위해 0.9로 불리게 되었다. HTTP/0.9는 단일 라인으로 구성되며 메서드도 GET이 유일했다.
GET /mypage.html
응답 또한 파일 내용 자체로 단순하게 구성됐다. 상태 코드도 없었다.
<HTML>
A very simple HTML page
</HTML>
2. HTTP/1.0
HTTP/1.0으로 오면서 Header, Status, Data(Content-Type)이 추가 되었다.
GET /mypage.html HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)
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>
3. HTTP/1.1
이때까지만해도 HTTP는 TCP 연결 기반 위에서 동작하는 프로토콜이었다. (HTTP/3부터는 아님)
신뢰성 확보를 위해 연결을 맺고 끊는데 있어 핸드셰이킹이 이루어진다. 연결을 맺고 끊을때마다 핸드셰이킹을 하기 때문에 오버헤드가 발생했다. 특이 모던 웹서비스들은 하이퍼 텍스트라기보다는 하이퍼 미디어로 발전했기 때문에 오버헤드가 커졌다. 웹서비스가 텍스트 위주에서 미디어들이 추가되고 상태(쿠키, 세션 등)을 유지하려는 기술들이 요구되다보니 성능 개선이 반드시 필요하게 되었다.
3-1) keep-alive (Persistent Connection)
keep-alive는 HTTP 요청과 응답 시, 다수의 TCP 연결을 줄이기 위한 방법이다. keep-alive를 사용하면 한번 맺어진 연결을 끊지 않고 지속적으로 유지하여 불필요한 핸드셰이킹을 줄일 수 있다. Persistent Connection라고도 부른다. keep-alive를 이용한 통신은 클라이언트나 서버 중 한쪽이 다음 헤더를 부여해 접속을 끊거나 타임아웃될 때까지 연결이 유지된다.
HTTP/1.0 기반에서 TCP 연결의 재사용을 원할때 아래처럼 요청 헤더 Connection 속성에 keep-alive를 세팅한다.
Connection: keep-alive
HTTP/1.1부터는 keep-alive가 기본으로 세팅된다. 따라서 keep-alive를 사용하지 않고 TCP 연결을 끊을 경우에만 Connection 속성을 설정하면된다.
Connection: close
3-2) 파이프라이닝 (Pipelining)
파이프라이닝은 하나의 커넥션에서 한번에 순차적인 여러 요청을 연속적으로 하고 그 순서에 맞춰 응답을 받는 방식으로 지연 시간을 줄이는 방법이다. keep-alive를 전제로 하며 서버는 요청이 들어온 순서대로 응답을 반환한다.
하지만 순차적으로 데이터를 요청하고 받아야 하다 보니 먼저 받은 요청이 끝나지 않으면 다음 요청의 처리가 빨리 끝나도 먼저 들어온 요청이 끝날때까지 기다려야한다. 이를 HOL(Head Of Line) Blocking이라고 부르며 파이프라이닝의 큰 단점이다. 그래서 모던 브라우저들은 대부분 파이프라이닝을 사용하지 못하도록 막아 놓았다.
3-3) Domain Sharding
파이프라이닝을 개선하기 위해 브라우저들은 여러개의 Connection을 생성해서 병렬로 요청을 보낸다. 이를 Domain Sharding이라고 부른다. 브라우저별로 Domain당 Connection 개수의 제한이 존재한다. 하지만 이것이 근본 해결책은 아니였고 HTTP/2에서 개선되게 된다.
4. HTTP/2
HTTP/1.x까지는 한번에 하나의 파일만 전송이 가능했다. 이로인해 여러 파일을 전송할 경우 선행하는 파일의 전송이 늦어지면 HOL Blocking이 발생하였다. HTTP/2에서는 이 문제를 해결하기 위해 여러 파일을 한번에 병렬로 전송한다.
4-1) 바이너리 프레이밍 계층
HTTP/1.x에서 HTTP 요청와 응답은 Message 단위로 구성되어 있었다. 이 Message는 Status, Header, Body 등으로 이루어져있고 요청과 응답에 필요한 정보를 담고 있었다.
HTTP/2/로 오면서 Message라는 단위 외에 Frame, Stream이라는 단위가 추가되었다.
- Frame : HTTP/2 통신상의 제일 작은 정보 단위이며 Header 혹은 Data이다.
- Message : HTTP/1.1과 마찬가지로 요청 혹은 응답의 단위이며 다수의 Frame으로 이주어졌다.
- Stream : 클라이언트와 서버 사이에 맺어진 연결을 통해 양방향으로 주고받는 하나 혹은 복수개의 Message이다.
여러개의 Frame이 모여 Message가 되고 Message 여러개가 모여 Stream이 되는 구조이다.
Frame < Message < Stream
4-2) 멀티플렉싱 (Multiplexing)
한개의 커넥션으로 옹시에 여러개의 메세지를 응답 순서에 상관없이 stream으로 주고 받는 것을 멀티플렉싱이라고 한다. HTTP/2에서는 위의 바이너리 프레이밍 계층을 사용하여 요청과 응답의 멀티플렉싱을 지원한다. Message를 바이너리 형태의 Frame으로 나누고 이를 전송 후 받은 쪽에서 다시 조립한다. 요청과 응답이 동시에 이루어지니 하나의 연결에 여러 요청과 응답이 뒤섞여있다. 프레이밍 작업은 서버와 클라이언트에서 해주기 때문에 큰 변경사항을 고려하지 않아도 된다. 바이너리 프레이밍과 멀티플렉싱을 이용하여 여러개의 연결없이 병렬처리를 한다.
4-3) Stream 우선순위 (Stream Prioritization)
HTTP Message가 많은 개별 Frame으로 분할되고 여러 Stream의 Frame을 멀티플렉싱 할 수 있게 되면서 Strema들의 우선순위를 지정할 필요가 생겼다. 클라이언트는 우선순위 지정을 위해 '우선순위 지정 트리'를 사용하여 서버의 Stream처리 우선순위를 지정할 수 있다. 서버는 우선순위가 높은 응답이 클라이언트에 우선적으로 전달될 수 있도록 대역폭을 설정한다.
4-4) HTTP 헤더 데이터 압축 (HTTP Header Data Compression)
HTTP/2에서는 이전 Message의 헤더의 내용 중 중복되는 필드를 재전송하지 않도록하여 데이터를 절약한다. 또한 기존에 HTTP 헤더가 Plain Text(평문)이었지만 HTTP/2에서는 Huffman Coding을 사용하는 HPACK이라는 Header 압축방식을 이용하여 데이터 전송 효율을 높였다.
4-5) Server Push (PUSH_PROMISE)
서버는 클라이언트의 요청에 대해 요청하지도 않은 리소스를 미리 보낼 수 있다. 가까운 미래에 특정 개체가 필요할때 사용될 수 있다.
예를들어 보통 홈페이지는 여러개의 리소스(CSS, Image 등)를 필요로한다. HTTP/1.1에서는 클라이언트는 요청한 HTML 문서를 수신한 후 HTML를 해석하면서 필요한 리소스를 요청하였다. HTTP/2에서 Server Push를 사용하염 아직 클라이언트가 요청하지 않은 리소스를 Push하여 미리 브라우저의 캐시에 가져다 놓을 수 있다. 이를 통해 성능 향상을 이끌어 낼 수 있다.
4-6) HTTP/1.1 vs HTTP/2 성능 비교
일반적으로 HTTP/2를 사용만해도 웹 응답 속도가 HTTP/1/1에 비해 15~50% 향상 된다고 한다. 아래는 동일 이미지를 웹사이트에 로딩시켜 HTTP/1.1과 HTTP/2의 속도를 비교한 결과이다. 아카마이에서 확인할 수 있다.
이제 HTTP/3에 대해서 알아보자.
참고
'개발' 카테고리의 다른 글
Nuxt 환경별 환경변수 구분하기 process.env (0) | 2022.02.27 |
---|---|
HTTP/3와 QUIC 알아보기 (0) | 2022.02.26 |
자바스크립트 키 동시 입력 이벤트 (0) | 2022.02.20 |
우분투 dpkg: error processing archive 에러 해결 (0) | 2022.02.19 |
웹팩 정리 (0) | 2022.01.31 |