포스트

HTTP의 진화

HTTP의 진화

HTTP의 진화

HTTP/0.9 - 원 라인 프로토콜

HTTP 초기 버전은 매우 단순했다. 요청은 단일 라인으로 구성되었으며, 사용할 수 있는 메서드도 GET 뿐이었다.

1
GET /myBlog.html

응답도 단순하여 오직 HTML 파일의 내용만 반환했다.

1
2
3
4
<html>
  A very simple Blog page
</html>

해당 버전에서는 HTTP 헤더가 없었기 때문에 HTML 파일만 전송이 가능했고, 다른 형식의 문서는 지원되지 않았다. 또한, 상태 코드가 없어 문제가 발생하면 해당 내용을 포함한 HTML 파일을 별도로 생성하여 사용자게에 보여줘야 됐다.


HTTP/1.0 - 확장성 확보

HTTP/0.9는 매우 제한적이었어서 브라우저와 서버가 빠르게 다양한 기능을 추가하기 시작했다.

  • 요청에 버전 정보(HTTP/1.0)를 포함하여 전송
  • 응답의 시작 부분에 상태 코드 추가 (예: 200 ok), 이를 통해 브라우저가 요청 성공 여부를 확인 가능
  • HTTP 헤더 개념 도입, 요청 및 응답에서 메타데이터 전송 가능
  • Content-Type 헤더 추가, HTML 외에도 다양한 형식의 문서 전송 가능

요청

1
2
GET /myBlog.html HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)

응답

1
2
3
4
5
6
7
8
9
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="/myBlog.gif">
</HTML>

이후, 이미지를 내려받기 위한 별도의 요청이 필요했다.

1
2
GET /myBlog.gif HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)

HTTP/1.1 - 표준화된 프로토콜

HTTP/1.1은 HTTP/1.0의 문제점을 해결하기 위해 나왔다.

주요 개선 사항

  • 연결 재사용(Persistent Connection) : 요청마다 새로운 연결을 여는 대신, 하나의 연결을 유지하여 여러 요청을 처리
  • 파이프라이닝(Pipelining) 지원 : 첫 번째 요청의 응답을 기다리지 않고 두 번째 요청을 전송할 수 있어 지연 시간 단축
  • 청크 전송 인코딩(Chunked Transfer Encoding) 지원
  • 추가적인 캐시 제어 매커니즘 도입
  • 콘텐츠 협상(Content Negotitaion) : 클라이언트와 서버가 언어, 인코딩, 문서 유형 등을 협상하여 가장 적절한 콘텐츠 제공
  • Host 헤더 추가 : 하나의 서버에서 여러 도메인을 호스팅 가능

1. 연결 재사용(Persistent Connection)

HTTP/1.1에서는 기본적으로 Keep-Alive라는 기능을 통해 연결을 재사용한다. 즉, 클라이언트가 서버에 요청을 보내고 응답을 받은 후에도 TCP 연결을 닫지 않고 유지한다. 이 연결을 통해 클라이언트는 추가적으로 요청을 계속 보낼 수 있다.

장점

  • TCP 연결 설정 오버헤드 감소 : 매번 새로운 요청을 보낼때마다 TCP 연결을 설정하는데 드는 시간과 리소스를 줄인다. 3-way 핸드셰이크 과정을 반복하지 않아도 된다.
  • 지연 시간 감소 : 기존 연결을 재사용하므로, 새로운 연결을 설정하는 데 걸리는 시간을 절약하여 응답 시간을 단축한다.

단점

  • 서버 자원 소모 : Keep-Alive 연결을 유지하는 동안 서버는 연결된 클라이언트에 대한 리소스를 계속 할당해야 한다. 너무 많은 연결이 유지되면 서버의 성능이 저하될 수 있다.
  • Head-of-Line-Blocking : HTTP/1.1에서는 여러 요청이 하나의 TCP 연결을 통해 순차적으로 처리된다. 만약 하나의 요청 처리 시간이 길어지면, 뒤에 있는 요청들도 함께 문제가 발생할 수 있다.

동작 방식

  1. 클라이언트가 서버에 HTTP/1.1 요청을 보낸다.
  2. 서버는 요청을 처리하고 응답을 클라이언트에게 보낸다.
  3. 응답 헤더에 Connection : keep-alive가 포함되어 있다면, 서버는 TCP 연결을 닫지 않고 유지한다.
  4. 클라이언트는 동일한 연결을 통해 추가적인 요청을 보낼 수 있다.
  5. 클라이언트 또는 서버가 Connection: close 헤더를 보내거나, 일정 시간 동안 연결이 유휴 상태로 유지되면 연결이 종료된다.

2. 파이프라이닝(Pipelining)

HTTP/1.1에서는 파이프라이닝은 클라이언트가 서버로부터 이전 요청에 대한 응답을 받기 전에 여러 개의 HTTP 요청을 연속적으로 전송하는 기술이다.

파이프라이닝이 없는 경우(Non-Pipelining)

  1. 클라이언트가 요청 A를 보낸다.
  2. 클라이언트는 요청 A에 대한 응답을 기다린다.
  3. 요청 A에 대한 응답을 받으면, 클라이언트는 요청 B를 보낸다.
  4. 클라이언트는 요청 B에 대한 응답을 기다린다.
  5. ….반복

파이프라이닝을 사용하는 경우(Pipelining)

  1. 클라이언트가 요청A, 요청B, 요청C를 순서대로 보낸다.(응답을 기다리지 않음)
  2. 서버는 요청A, 요청B, 요청C를 순서대로 처리한다.
  3. 서버는 요청A에 대한 응답, 요청B에 대한 응답, 요청C에 대한 응답을 순서대로 클라이언트에게 보낸다.

장점

  • 지연 시간 감소: 여러 요청에 대한 응답을 기다리는 시간을 줄여 전체적인 페이지 로딩 속도를 향상시킬 수 있다.
  • 네트워크 효율성: 여러 요청을 하나의 TCP 연결을 통해 전송하므로, TCP 연결 설정 오버헤드를 줄일 수 있다.

파이프라이닝 제약 사항 및 문제점

  • 순서대로 처리 및 응답: 서버는 요청을 받은 순서대로 처리하고, 응답도 받은 순서대로 보내야 한다. 만약 요청 A의 처리가 지연되면, 요청 B와 요청 C에 대한 응답도 함께 지연되는 Head-of-Line Blocking (HOL Blocking) 문제가 발생할 수 있다.
  • 멱등성 (Idempotency) 보장: 파이프라이닝은 GET, HEAD, OPTIONS, TRACE와 같이 멱등성(여러 번 요청해도 결과가 동일함)이 보장되는 메서드에만 사용해야 한다. POST와 같은 멱등성이 보장되지 않는 메서드는 파이프라이닝에 사용하면 예기치 않은 문제가 발생할 수 있다.

3. 청크 전송 인코딩 (Chunked Transfer Encoding)

청크 전송 인코딩은 HTTP/1.1에서 데이터를 전송하는 방식 중 하나이다. 주로, 서버가 응답 본문의 전체 크기를 미리 알 수 없는 경우에 유용하게 사용된다.

기존 HTTP 전송 방식의 문제점

  • HTTP/1.1이전에는 응답 헤더에 Content-Length 헤더를 포함하여 본문의 전체 크기를 명시해야 했다. 하지만 동적으로 생성되는 콘텐츠나 스트리밍 데이터와 같이 서버가 본문의 크기를 미리 알 수 없는 경우에는 Content-Length 헤더를 사용할 수 없었다.

청크 전송 인코딩의 등장

  • 청크 전송 인코딩은 이러한 문제를 해결하기 위해 도입되었다. 해당 방식에서는 응답 본문을 여러 개의 “청크(Chunk)”로 나누어 전송하며, 각 청크의 크기와 데이터를 함께 보낸다.

청크 전송 인코딩의 동작 방식

  1. Transfer-Encoding: chuncked 헤더 추가
    • 서버는 응답 헤더에 Transfer-Encoding: chuncked 헤더를 추가하여 청크 전송 인코딩을 사용함을 알린다.
  2. 청크 전송 : 응답 본문을 다음과 같은 형식으로 나누어 전송한다.
    • [청크 크기 (16진수)]\r\n
    • [청크 데이터]\r\n
  3. 마지막 청크 : 마지막 청크는 크기가 0인 청크로 표시한다.
    • 0\r\n
    • [트레일러 헤더 (선택 사항)]\r\n
    • \r\n
    • 크기가 0인 청크는 전송이 완료되었음을 나타낸다. 선택적으로 트레일러 헤더를 포함할 수 있으며, 마지막 빈 라인으로 마무리된다.

📌 예시

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

7\r\n
Mozilla\r\n           // 크기가 7바이트인 "Mozilla" 데이터
9\r\n
Developer\r\n         // 크기가 9바이트인 "Developer" 데이터
7\r\n
Network\r\n           // 크기가 7바이트인 "Network" 데이터
0\r\n
\r\n                  // 마지막 청크 (전송 완료)

청크 전송 인코딩의 장점

  • 동적 콘텐츠 지원 : 서버가 응답 본문의 전체 크기를 미리 알 수 없는 경우에도 데이터를 전송할 수 있다.
  • 스트리밍 데이터 지원 : 실시간으로 생성되는 스트리밍 데이터를 클라이언트에 전송할 수 있다.
  • 지연 시간 감소 : 전체 데이터를 버퍼링하지 않고 청크 단위로 전송하므로, 클라이언트는 데이터를 더 빨리 받아서 처리할 수 있다.

청크 전송 인코딩의 단점

  • 오버헤드 증가 : 각 청크마다 크기 정보를 함께 보내야 하므로, 전송되는 데이터의 양이 증가한다.
  • 구현 복잡성 : 클라이언트와 서버 모두 청크 전송 인코딩을 처리하는 로직을 구현해야한다. (안쓸듯…)

4. Host 헤더 추가

HTTP/1.1에서 Host 헤더는 클라이언트가 요청하는 서버의 호스트 이름(Host Name)을 명시적으로 지정하는데 사용되는 요청 헤어딩다. HTTP/1.1부터는 모든 HTTP 요청에 Host 헤더가 필수적으로 포함되어야 한다. 이 헤더는 가상 호스팅(Virtual Hosting) 환경에서 중요한 역할을 한다.

HTTP/1.0의 한계

  • HTTP/1.0에서는 Host 헤더가 필수가 아니었다. 따라서 하나의 IP 주소로 여러 웹 사이트를 호스팅 하는 가상 호스팅 환경에서 서버는 어떤 웹 사이트에 대한 요청인지 정확하게 식별할 수 없었다.

Host 헤더의 등장

  • HTTP/1.1에서는 이러한 문제를 해결하기 위해 Host 헤더를 필수로 지정했다. 클라이언트는 Host 헤더를 통해 요청하는 서버의 호스트 이름을 명시적으로 지정할 수 있으며, 서버는 이 정보를 사용하여 요청을 올바른 웹 사이트로 라우팅 할 수 있게 되었다.

Host 헤더 구문

1
Host: <호스트 이름>[:<포트번호>]
  • <호스트 이름=""> : 요청하는 서버의 호스트 이름(예 : imkh817.github.io,example.com)
  • [:<포트번호>] : 선택적으로 포트 번호를 지정할 수 있다. 포트 번호를 생략하면 기본 포트가(HTTP:80, HTTPS:443) 사용된다.

Host 헤더의 중요성

  1. 가상 호스팅 지원
    • Host 헤더는 하나의 IP 주소로 여러 웹 사이트를 호스팅하는 가상 호스팅 환경에서 필수적이다.
    • 서버는 Host 헤더를 사용하여 올바른 웹 사이트로 라우팅하고, 해당 웹 사이트에 대한 응답을 제공할 수 있다.
    • 예를 들어, 동일한 IP 주소를 사용하는 두 개의 웹사이트 www.exmaple.comwww.example.net이 있다고 할 때, 클라이언트가 www.example.com에 대한 요청을 보내면, Host: www.example.com 헤더가 요청에 포함된다. 서버는 이 헤더를 사용하여 www.example.com에 대한 요청임을 식별하고, 해당 웹 사이트의 콘텐츠를 제공한다.
  2. 프록시 서버 지원
    • 프록시 서버는 Host 헤더를 사용하여 요청을 적절한 백엔드 서버로 라우팅할 수 있다.

HTTP/1.1 예 (하나의 단일 커넥션을 통한 요청의 전형적인 전체 흐름)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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

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

(content)

GET /static/img/header-background.png 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: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/en-US/docs/Glossary/Simple_header

200 OK
Age: 9578461
Cache-Control: public, max-age=315360000
Connection: keep-alive
Content-Length: 3077
Content-Type: image/png
Date: Thu, 31 Mar 2016 13:34:46 GMT
Last-Modified: Wed, 21 Oct 2015 18:27:50 GMT
Server: Apache

(image content of 3077 bytes)

HTTP/2

HTTP/2 탄생 배경

기존의 HTTP/1.1은 웹이 발전하면서 여러 성능상의 한계가 드러났다. 대표적인 문제는 다음과 같다.

  1. 연결당 요청/응답의 직렬화 문제(Head-of-Line Blocking)
    • HTTP/1.1에서는 하나의 연결에서 요청을 순차적으로 처리해야 했다.
    • 따라서 첫 번째 요청이 응답을 받기 전까지 다음 요청이 대기해야 하는 문제가 발생했다.
    • 이를 해결하기 위해 브라우저들은 여러 개의 TCP 연결을 생성했지만, 이는 오히려 네트워크 성능을 저하시킨다.
  2. 비효율적인 헤더 전송(Header Overhead)
    • HTTP 요청과 응답에는 불필요한 헤더 정보가 반복적으로 포함되어 네트워크 대역폭을 낭비했다.
  3. 병렬 처리의 어려움
    • 여러 개의 리소스를 동시에 가져오기 위해 브라우저가 여러 개의 TCP 연결을 열었지만, 연결 개수가 제한적이서 병목이 발생한다.
  4. 서버 푸시 기능 부재
    • HTTP/1.1에서는 클라이언트가 요청을 보내야만 서버가 응답을 반환할 수 있었다.
    • 따라서 서버가 클라이언트에게 미리 필요한 리소스를 전송하느 기능이 없었다.

이러한 문제를 해결하기 위해 Google의 SPDY 프롵토콜이 개발되었으며, 이를 기반으로 HTTP/2가 표준으로 제정되었다.

HTTP/2의 주요 특징

HTTP/2는 기존 HTTP의 문법은 유지하면서도 성능을 대폭 개선하였다.

  1. Multiplexing(다중화)
    • 하나의 TCP 연결을 통해 여러 개의 요청과 응답을 동시에 송수신할 수 있다.
    • HTTP/1.1에서는 하나의 요청이 끝날 때까지 다음 요청이 대기해야 했지만, HTTP/2에서는 동시에 여러 개의 요청을 처리할 수 있다.
    • 이를 통해 요청 간 지연 시간이 줄어들고 성능이 향상된다.
  2. Header Compression(헤더 압축)
    • HTTP/2는 HPACK이라는 압축 방식을 사용하여 헤더 크기를 줄인다.
    • 동일한 헤더가 반복될 경우 중복을 제거하고, 인코딩 기법을 적용하여 네트워크 대역폭을 절약한다.
  3. Stream Prioritization(스트림 우선순위)
    • 요청(스트림)마다 우선순위를 부여하고 중요한 리소스를 먼저 다운로드할 수 있다.
    • 예를 들어, CSS나 JavaScript 파일을 먼저 받아오고, 이미지 리소스는 나중에 받아올 수 있다.
  4. Server Push(서버 푸쉬)
    • 클라이언트가 요청하지 않아도, 서버가 미리 필요한 리소스를 전송할 수 있다.
    • 예를 들어, HTML을 요청하면 서버가 CSS와 JavaScript 파일도 함께 보내줄 수 있어 페이지 로딩 속도가 빨라진다.
  5. Binary Protocol(이진 프로토콜)
    • HTTP/1.1은 텍스트 기반 프로토콜이지만, HTTP/2는 이진 프로토콜을 사용한다.
      • 이진 형식 덕분에 전송이 최적화되고, 파싱 속도가 빨라진다.
        • 예시
          • HTTP/1.1의 요청 예시(텍스트 기반)
            1
            2
            3
            4
            
            GET /index.html HTTP/1.1
            Host: www.example.com
            User-Agent: Mozilla/5.0
            Accept: text/html
            
          • HTTP/2의 요청 예시(이진 프로토콜)
            1
            2
            3
            4
            
            00000001 00000000 00011000 00000011 10100000 00000000
            11001001 01101000 10101011 11010100 00101101 10110110
            ...
            
            

HTTP/2의 작동 방식

HTTP/2는 기존과 달리 하나의 연결에서 프레임 단위로 데이털르 주고 받는 방식을 사용한다.

  • 스트림(Stream) : HTTP/2에서 요청/응답 단위를 의미하며, 여러 개의 스트림이 동시에 처리될 수 있다.
  • 메시지(Message) : 요청 또는 응답 자체를 의미하며, 여러 개의 프레임으로 구성된다.
  • 프레임(Frame) : HTTP/2에서 데이터를 송수신하는 가장 작은 단위로, 헤더 프레임과 데이터 프레임으로 나뉜다. 즉, 하나의 TCP 연결에서 여러 개의 스트림이 동시에 처리되고, 각 스트림은 여러개의 프레임으로 나누어 전송한다.

HTTP/2의 한계

HTTP/2는 다양한 기능을 가지고 있지만, 몇가지 한계점도 존재한다.

  1. TLS(HTTPS) 의존성
    • HTTP/2는 HTTPS(SSL/TLS 암호화)를 필수적으로 요구하지는 않지만, 대부분의 브라우저는 HTTP/2를 사용하려면 TLS를 강제한다.
    • 따라서 HTTP/1.1에 비해 추가적인 암호화 오버헤드가 발생할 수 있따.
  2. Head-of-Line-Blocking(TCP 레벨)
    • HTTP/2에서 Multiplexing을 사용하여 요청을 동시에 처리할 수 있지만, 여전히 하나의 TCP 연결을 공유하기 때문에 패킷 손실이 발생하면 모든 요청이 지연될 수 있다.
    • 이를 해결하기 위해 HTTP/3에서는 QUIC 프로토콜을 도입했다.

HTTP/1.1 vs HTTP/2 비교

헤더 1HTTP/1.1HTTP/2
요청/응답 방식직렬화된 요청(하나씩 처리)다중화(Multiplexing)
헤더 전송중복된 헤더 포함HPACK 압축 사용
우선 순위 설정없음Stream Prioritizaion 지원
서버 푸시없음가능
전송 방식텍스트 기반이진(Binary) 프레임 기반

HTTP/3

HTTP/3는 기존 TCP 기반의 HTTP/1.1 및 HTTP/2의 한계를 극복하고 웹 성능을 더욱 향상시키기 위해 개발된 최신 HTTP 프토콜이다. 핵심적으로 QUIC(Quick UDP Internet connections)라는 새로운 전송 프로토콜을 사용하여 성능,보안, 안정성을 개선했다.

HTTP/3 등장 배경

HTTP/2는 멀리플렉싱, 헤더 압축 등의 기술을 통해 HTTP/1.1의 성능을 크게 향상시켰지만, TCP 프로토콜 자체의 한계로 인해 여전히 몇 가지 문제점을 가지고 있다.

  • TCP HOL(Head-of-Line-Blocking) : TCP는 패킷 손실이 발생하면 손실된 패킷 이후의 모든 패킷의 전달을 멈추고 재전송을 기다린다. HTTP/2는 멀티플렉싱을 통해 여러 스트림을 하나의 TCP 연결에서 처리하지만, TCP 레벨에서 HOL Blokcing이 발생하면 모든 스트림이 함께 지연되는 문제가 있다.
  • TCP 핸드셰이크 오버헤드 : 새로운 TCP 연결을 설정하느 데는 3-way 핸드셰이크 과정이 필요하며, 이는 추가적인 지연 시간을 발생시킨다. 특히 모바일 환경과 같이 네트워크 연결이 불안정한 환경에서는 연결 설정 오버헤드가 더욱 크게 작용된다.
  • 고정된 프로토콜 : TCP는 운영체제 커널 레벨에서 구현되기 때문에, 새로운 기능을 추가하거나 프로토콜을 개선하는데 어려움이 있다.

이러한 문제점을 해결하기 위해 HTTP/3는 TCP 대신 UDP 기반의 QUIC 프로토콜을 사용하여 개발되었다.

QUIC (Quick UDP Internet Connections) 프로토콜

QUIC는 Google에서 개발한 새로운 전송 프로토콜로, 다음과 같은 특징을 가지고 있다.

  • UDP 기반 : UDP는 TCP와 달리 연결 설정 과정이 없고, 패킷 손실에 대한 복구 기능이 없어 빠르고 유연하다.
  • 신뢰성 있는 전송 : QUIC는 UDP 위에서 신뢰성 있는 전송을 제공하기 위해 자체적인 오류 제어, 흐름 제어, 혼잡 제어 메커니즘을 구현한다.
  • 연결 마이그레이션 : QUIC는 연결 ID를 사용하여 연결을 식별하므로, 클라이언트 IP 주소가 변경되더라도 연결을 유지할 수 있다.
  • 스트림 멀티플렉싱 : QUIC는 여러 개의 스트림을 하나의 UDP 연결에서 동시에 처리할 수 있다. 이를 통해 HOL Blocking 문제를 해결하고, 웹 페이지 로딩 속도를 향상시킨다.

HTTP/3 주요 특징

HTTP/3는 QUIC 프로토콜을 기반으로 다음과 같은 주요 특징을 가진다.

  • QUIC 기반 전송 : TCP 대신 QUIC 프로토콜을 사용하여 성능,보안,안정성을 획기적으로 개선
  • HOL Blocking 해결 : QUIC는 스트림 레벨에서 독립적인 오류 복구 기능을 제공하므로, 특정 스트림에서 패킷 손실이 발생하더라도 다른 스트림에는 영향을 미치지 않는다.
  • 연결 마이그레이션 : 클라이언트 IP 주소가 변경되더라도 연결을 유지한다.
  • 향상된 보안 : TLS 1.3을 사용하여 모든 통신 내용을 암호화하고, 중간자 공격을 방지한다.
  • 빠른 연결 설정 : QUIC는 O-RTT (Zero Round Trip Time) 연결 설정을 지원하여 초기 연결 설정 시간을 단축한다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.