본문 바로가기
Computer Science/Computer Network

[Network] HTTP/3 과 QUIC 에 대해서

by domo7304 2022. 12. 29.

1. 왜 HTTP/3 이 필요하게 되었나? (이동)
    1. handshake 과정
    2. TCP HOL (Head Of Line) Blocking 문제
    3. TCP 를 발전시키는 게 불가능했던 이유
    - 요약
2. QUIC 이란 무엇인가? (이동)
    - 요약
3. QUIC 의 특징 (이동)
    1. TLS 없이는 QUIC 도 없다
    2. QUIC 은 여러 개의 스트림을 인지할 수 있다.
        - 요약
    3. QUIC 은 연결 마이그레이션을 지원한다.

HTTP/1.0, HTTP/1.1, HTTP/2 의 차이점을 조사하다 HTTP/3 과 QUIC 이라는 프로토콜을 접하게 되었는데, 정말 재미있는 글을 발견해서 기억할만한 내용들을 번역 해두어야겠다고 생각했다.

예를 들면, 현대 웹을 위한 새로운 프로토콜이 필요하게 되었을 때 왜 TCP 가 아니라 UDP 위에 새로운 프로토콜을 구현하게 되었는지, 그리고 사람들이 잘못 알고 있는 만연한 착각은 무엇인지 (2. QUIC 이란 무엇인가? (이동) 에서 다룬다.) 등 기술적인 내용에 더하여 기술이 왜 그렇게 발전하였는지와 같은 관점의 내용도 많이 담겨 있어 흥미롭게 읽었다.

원문의 의도를 해치지 않기 위해 최대한 직역에 가깝게 해석하였고, 의역이 필요하다면 본문과 표현이 조금 다르더라도 뉘앙스를 최대한 전달하고자 노력했다.

https://www.smashingmagazine.com/2021/08/http3-core-concepts-part1/

 

HTTP/3 From A To Z: Core Concepts — Smashing Magazine

What exactly is HTTP/3? Why was it needed so soon after HTTP/2 (which was only finalized in 2015)? How can or should you use it? And especially, how does this improve web performance? Let’s find out.

www.smashingmagazine.com

 

1. 왜 HTTP/3 이 필요하게 되었나?


1. handshake 과정

TCP 는 새로운 연결을 맺기 위해 handshake 과정이 필요하다. 이것은 클라이언트와 서버가 존재하고, 그들이 데이터를 교환할 준비가 되었다는 것을 보장하기 위해 수행된다. 이러한 handshake 는 전체 네트워크를 왕복해야만 한다. 만약 클라이언트와 서버가 지리적으로 멀리 떨어져있다면, 각 RTT (Round Trip Time) 는 눈에 띌 정도로 발생할 수도 있다.

2. TCP HOL (Head Of Line) Blocking 문제

두 번째 예시로, 우리가 실제로는 여러 개의 파일을 동시에 전송하기 위해 TCP 를 사용할지라도, TCP 는 자신이 전송하는 모든 데이터를 하나의 file로 본다 (이것이 정확히 어떤 의미인지는 뒤에서 자세히 설명한다.). 이것은 만약 하나의 파일의 데이터를 포함하는TCP 패킷이 손실된다면 손실된 패킷이 복구될 때까지 다른 모든 파일들도 지연된다는 것을 의미한다. 이를 HOL (Head Of Line) Blocking 문제라고 한다.

3. TCP 를 발전시키는 게 불가능했던 이유

우리는 이러한 문제들을 개선하기 위해 TCP 를 업그레이드하기 위해 노력했으며, 새로운 기능을 개발하기도 하였다.

예를 들어, TCP Fast Open 은 시작부터 상위 계층 프로토콜이 데이터를 전송할 수 있도록 함으로써 handshake 오버헤드를 제거했다. 이러한 TCP extension 을 구현하는 것은 그리 어렵지 않았다. 그러나, 이러한 것들을 실제 인터넷 규모로 배포하는 것은 매우 어려웠다.

TCP 는 매우 널리 사용되기 때문에, 인터넷에 연결된 대부분의 모든 장치들은 프로토콜 자체 구현을 갖고 있다. 만약 이러한 구현이 너무 오래되었거나, 업데이트가 없거나 한 경우 extension 을 구현했더라도 실제로 쓸 수 없을 수도 있다.

만약 엔드포인트 장치들 (우리의 PC, 스마트폰 등)에 대해서만 이야기 한다면, 그것들은 상대적으로 쉽게 수동 업데이트가 가능하기 때문에 이것은 그렇게 큰 문제는 아니다. 하지만 많은 다른 장치들 (방화벽, 로드밸런서, 라우터, 캐싱서버, 프록시 등) 은 클라이언트와 서버 사이에 위치해있으며, 그들 자체적인 TCP 구현을 갖고 있다.

이러한 미들박스들은 업데이트가 더욱 어렵다. 결과적으로, 미들박스들의 TCP 구현이 실제로 추가기능을 사용하기 위해 업데이트 되는 것은 매우 오랜 시간이 걸릴 수 있다. 때문에 TCP 를 진화시키는 것은 거의 불가능하다고 할 수 있다.

결과적으로, 우리는 handshaking 으로 인한 지연, TCP HOL Blocking 등의 문제를 해결하기 위해 TCP 를 직접적으로 업그레이드 하기보다는 프로토콜을 대체해야 했다.

※ 요약

우리가 필요했던 것은 사실 HTTP/3 가 아니라 새로운 “TCP/2” 였고, 우리는 그 과정에서 HTTP/3 를 얻게 된 것이다. HTTP/3 의 주요 기능 faster connection set-up, 더 적은 HOL Blocking, connection migration 등은 HTTP/3 로부터가 아니라 모두 QUIC 으로부터 온 것이다.

 

2. QUIC 이란 무엇인가?

QUIC 이 UDP (User Datagram Protocol) 위에서 동작한다는 것을 들어보았을 것이다. 이것은 사실이다, 그러나 많은 사람들이 주장하는 성능 이유 때문은 아니다.

UDP 는 가장 기본적인 전송 프로토콜이다. UDP 는 소위 포트 번호를 제외하고는 어떠한 기능들도 제공하지 않는다. UDP 는 handshake 로 연결을 설정하지 않는다. 때문에 handshake 를 위해 기다릴 필요도 없고, HOL Blocking 도 존재하지 않는다.

QUIC 을 독립적인 새로운 프로토콜로 만들고자 했다면, TCP 를 업그레이드 하는 것이 어려웠던 이유와 동일한 문제가 발생했을 것이다 (QUIC 을 인식하고, 받아들이기 위해서는 첫 번째로 인터넷의 모든 장치들이 업데이트 되어야만 한다.). 운이 좋게도 인터넷에서 광범위하게 지원되는 다른 하나의 전송 계층 프로토콜인 UDP 가 있었고, 이 UDP 는 포트 번호를 제외하고 어떠한 기능도 제공하지 않기 때문에 UDP 위에 QUIC 을 구축할 수 있었다.

많은 곳에서 HTTP/3 는 성능 때문에 UDP 위에 구축되었다고 말한다. 그들은 HTTP/3 가 UDP 처럼 connection 을 구축하지 않고, packet 의 재전송을 기다릴 필요가 없어서 더 빠르다고 한다. 그러나 이러한 이야기는 잘못되었다. 우리가 바로 위에서 이야기했던 것처럼, UDP 는 이미 인터넷의 거의 모든 장치들에 구현되어있고, 알려져 있으며, QUIC 과 HTTP/3 의 배포가 더 쉬워지기를 희망하기 때문에 QUIC 은 UDP 위에 구축되었다.

※ 요약

우리는 QUIC 이라 불리는 훨씬 더 진보된 버전의 TCP 를 구현했다. 그리고 QUIC 이 더 쉽게 배포되길 원했기 때문에, QUIC 을 UDP 위에서 구축하였다. HTTP/3 는 TCP 를 UDP 로 바꿨다고 해서 HTTP/2 보다 마법처럼 빠른 것은 아니다.

 

3. QUIC 의 특징

  1. QUIC 은 TLS 와 긴밀하게 통합되었다.
  2. QUIC 은 여러 개의 독립 바이트 스트림을 지원한다.
  3. QUIC 은 connection ID 를 사용한다.
  4. QUIC 은 frame 을 사용한다.

1. TLS 없이는 QUIC 도 없다.

TLS 는 인터넷에서 데이터 전송을 보호하고 암호화하는 역할을 맡는다. 우리가 HTTPS 를 이용할 때, 우리의 HTTP plaintext (평문) 은 TCP 로 전달되기 이전에 TLS 에 의해 암호화된다.

인터넷 초창기에, 통신을 암호화하는 것은 꽤나 큰 비용이었으며, 모든 사용 사례에 필요하다고 여겨지지도 않았었다. 따라서 TLS 는 TCP 위에서 선택적으로 사용될 수 있도록 완벽히 별개의 프로토콜이 되었다.

시간이 지나며 우리는 보안이 매우 기본적인 것이라 생각하게 되었다. 예를 들어, 이론적으로 HTTP/2 는 TLS 없이 TCP 를 통해 직접적으로 실행될 수 있지만, 실제로 이러한 모드를 지원하는 웹브라우저는 없다.

항상 TLS 를 이용하도록 향하는 발전 방향을 고려해보았을 때, QUIC 이 TLS 자체를 직접 사용하도록 디자인된 것은 그리 놀라운 일이 아니다.

TLS 는 TCP 위에서 독립적으로 실행될 수 있는 것에 반해, QUIC 은 TLS 를 캡슐화했다. 때문에 QUIC 은 항상 완전히 암호화된다. 또한 QUIC 은 거의 모든 packet header 들을 암호화하기 때문에, TCP 에서는 암호화되지 않는 packet number 와 같은 전송 계층 정보는 더 이상 중간자 (해커, 공격자 등) 에 의해 읽히지 않는다.

QUIC 은 TCP 와 마찬가지로 처음에 TLS handshake 를 이용한다. 그러나 이후에 TCP 위의 TLS 에서는 TLS 가 암호화를 수행하는 반면, QUIC 은 packet 들을 스스로 암호화한다. 이러한 차이를 통해 다음과 같은 이점들을 얻을 수 있다.

  1. QUIC 은 더욱 안전하다.
    QUIC 은 암호화되지 않은 데이터로 실행될 수 없기 때문에(항상 암호화되어 전달되기 때문에), 공격자가 취할 수 있는 방법이 더욱 적다.
  2. QUIC 의 연결 설정은 더욱 빠르다.
    TCP 위의 TLS 에서는 TCP handshake 와 TLS handshake 가 각각 이뤄져야 하는 반면, QUIC 은 전송과 암호화를 위한 handshake 가 하나로 결합되어 있기 때문에 RTT 를 줄일 수 있다.

하지만 다음과 같은 단점도 생각할 수 있다.

  1. QUIC 은 큰 암호화 오버헤드를 가질 수 있다.
    TCP 위의 TLS 는 동시에 여러 packet 들을 암호화할 수 있는 반면, QUIC 은 각 개별적인 packet 들을 암호화하기 때문에, 많은 처리량을 요구하게 될 경우 잠재적으로 QUIC 은 느릴 수 있다.

2. QUIC 은 여러 개의 스트림을 인지할 수 있다.

HTTP/1.1 에서 리소스를 가져오는 과정은 매우 간단했다. 각 파일은 그들을 위한 TCP 연결이 주어졌기 때문이다. 예를 들어 우리가 A, B, C 라는 세 개의 파일을 갖고 있다면, 파일을 전송하기 위해서 3개의 TCP 연결이 생성되었던 것이다. 새로운 연결을 생성하는 것은 오버헤드가 있기 때문에 이것은 매우 비효율적이다.

때문에 실제 상황에서 브라우저는 사용할 수 있는 동시 연결 수를 전형적으로 6개에서 30개 사이로 제한한다. 이러한 제한은 결국 30개보다 더 많은 리소스들을 로드하는 현대 웹 페이지의 성능을 방해하기 시작했다.

HTTP/1.1 does not allow multiplexing, unlike both HTTP/2 and HTTP/3.

이러한 상황을 개선하는 것은 HTTP/2 의 주요 목적 중 하나였다. HTTP/2 로 인해 더 이상 각 파일을 위해 새로운 TCP connection 을 열 필요가 없어졌으며, 다른 리소스들을 하나의 TCP connection 을 통해 다운받을 수 있게 되었다. 이것은 각기 다른 바이트 스트림들을 multiplexing 함으로써 가능하게 되었다.

그러나 이는 TCP 측면에서 문제가 있다. 우리가 알다시피 TCP 는 매우 오래된 프로토콜이며, 웹 페이지를 로드하기 위해 만들어지지 않았기 때문에 현재 패킷이 전송 중인 A, B, C 라는 파일을 구분하지 못한다. TCP 는 단지 패킷 하나를 전송할 때 파일 하나를 전송한다고 생각하고 옮길 뿐이다. 대부분의 상황에서 이는 문제가 되지 않지만, 패킷 손실이 발생한다면 문제가 발생한다.

만약 A, B, C 라는 세 개의 리소스가 전송 중이고, A 와 B 에 대한 데이터는 도착하였으나, C 에 대한 데이터를 담은 TCP packet 일부가 손실되었다고 하자. 이 경우 TCP 는 손실된 데이터의 새로운 복사본을 재전송한다.

우리는 A 와 C 에 손실이 발생하지 않았기 때문에, B 를 기다리며 A 와 C 를 처리하면 된다고 생각할 수 있지만, 슬프게도 그렇지 않다.

TCP layer 에서 이들은 단순히 패킷이므로 TCP 는 A 와 B 와 C 를 알지 못한다. TCP 는 패킷의 일부가 손실되었을 때, 패킷에 포함된 다른 일부 데이터들은 정상적으로 처리될 수 있음에도 불구하고, TCP 는 해당 패킷을 전부 정상적으로 수신할 때까지 해당 패킷을 처리하지 않는다. 우리는 A 와 B 가 처리될 수 있다는 것을 알지만, TCP 는 A, B, C 의 데이터가 담긴 패킷이 전부 정상적으로 도착해야 처리를 할 수 있는 것이다. 이것이 바로 TCP HOL Blocking 문제이다.

전송 계층에서의 HOL Blocking 문제를 해결하는 것은 QUIC 의 중요한 목적들 중 하나이다. TCP 와 달리 QUIC 은 여러 개의 독립적인 바이트 스트림을 multiplexing 한다는 것을 알고 있다. 때문에 위의 예시에서 TCP 와 달리 QUIC 은 A 와 B 에 대한 데이터는 HTTP/3 계층에 전달할 것이고, C 에 대한 데이터만 다시 요청할 것이다. 이론적으로 이를 통해 성능 향상을 이룰 수 있다.

QUIC allows HTTP/3 to bypass the head-of-line blocking problem.

우리는 이제 TCP 와 QUIC 이 근본적으로 다르다는 것을 볼 수 있다. 이것이 우리가 QUIC 위에서 HTTP/2 를 실행하지 못하는 이유 중 하나이기도 하다. HTTP/2 도 단일 TCP connection 에서의 multiplexing 개념을 포함하며, HTTP/2 와 QUIC 이 함께 동작하도록 하는 것은 매우 어렵다. 때문에, HTTP/2 와 HTTP/3 의 주요 차이점 중 하나는 HTTP/3 에서 HTTP stream logic 을 제거하고 대신 QUIC stream logic 을 사용했다는 것이다.

※ 요약

여기서의 중요한 점은, TCP 는 하나의 connection 안에서 여러개의 “독립적인” 파일이 전송되도록 디자인되지 않았다는 것이다. QUIC 은 여러 개의 바이트 스트림에서 스트림 단위로 패킷 손실을 처리함으로써 이를 해결하였다.

3. QUIC 은 연결 마이그레이션을 지원한다.

QUIC 의 세 번째 중요한 개선은 connection 이 더 오래 유지될 수 있다는 사실이다.

TCP 에서 connection 은 client IP 주소, client 포트, server IP 주소, server 포트 4가지 요소로 식별된다. 때문에 위 4가지 중 하나만 바뀌더라도 이전의 connection 과는 다른 connection 으로 인식되고, 새로운 연결 수립 과정이 필요하게 된다.

Once the client gets a new IP, the server can no longer link it to the connection.

예를 들어, 우리가 스마트폰을 건물 안의 Wi-Fi 를 통해 사용중이라고 하자. 그렇다면 우리는 이 Wi-Fi 네트워크에서의 IP address 를 할당받을 것이다. 이후에 우리가 밖으로 나가서 Wi-Fi 를 끄고 4G 혹은 5G 데이터를 켰다고 하자. 이 경우 우리는 완전히 새로운 IP address 를 할당받게 될 것이다. 이 때, 서버는 클라이언트 IP 로부터 온 TCP packet 을 이전에 본 적 없는 완전히 새로운 것이라고 생각한다.

그렇다면 서버는 이 packet 들이 다른 client 로부터의 새로운 연결에서 온 것이 아니라는 것을 어떻게 알 수 있을까? 슬프게도, 그러한 방법은 없다.

TCP 는 우리가 스마트폰 등을 사용할 것이라고 꿈꾸기 이전에 개발되었다. 때문에 클라이언트가 서버에게 자신의 IP 가 바뀌었음을 알릴 어떠한 메커니즘도 존재하지 않는다.

새로운 TCP handshake 는 새로운 connection 을 설정하기 위해 실행되어야만 하고, 현재 진행 중인 작업은 다시 시작되어야만 한다. 예를 들어 만약 우리가 큰 사이즈의 파일을 HTTP 위에서 다운 받는 중이라고 하자, 서버가 range requests 를 지원하지 않는다면 해당 파일을 처음부터 다시 요청해야할 수도 있다.

TCP 연결을 새로 시작하는 것은 성능에 큰 영향을 줄 수 있다. 이러한 문제를 해결하기 위해 QUIC 은 connection identifier (CID) 라는 이름의 새로운 개념을 도입했다. 각 connection 은 server 와 client 사이의 connection 을 고유하게 식별하는 다른 숫자가 할당된다.

QUIC uses connection identifiers (CIDs) to allow connections to survive a network change.

결정적으로, 이 CID 는 QUIC 자체의 전송 계층에서 정의되기 때문에, 네트워크 사이를 이동할 때 변경되지 않는다. 이것이 가능하도록 하기 위해, CID 는 모든 QUIC packet 의 앞에 포함된다.

이러한 설정을 통해 client IP 주소, client 포트, server IP 주소, server 포트 중 하나가 바뀌더라도, QUIC server 와 client 는 이 connection 이 원래 존재하던 connection 인지 알기 위해 CID 만 확인하면 된다. 때문에 새로운 handshake 는 필요하지 않으며, 진행 중이던 작업 상태 (ex. 다운로드) 를 그대로 유지할 수 있다. 이러한 특성은 connection migration 이라 불린다.

하지만 CID 가 극복해야하는 다른 어려움이 있다. 예를 들어, 우리가 정말로 하나의 CID 만 사용한다면, 해커가 사용자를 추적하는 것이 매우 쉬워질 것이다. 이러한 현상을 막기 위해, QUIC 은 새로운 네트워크가 사용될 때마다 CID 를 바꾼다.

QUIC uses multiple negotiated connection identifiers (CIDs) to prevent user tracking

내부적으로 실제 일어나는 것은 client 와 server 가 랜덤으로 생성된 CID 공통 리스트에 동의하는 것이다. 예를 들어, server 와 client 가 K, C, D 라는 CID 가 X 라는 connection 에 매핑된다는 것을 알고 있다고 하자. client 는 Wi-Fi 를 사용 중일 때 K 라는 CID 를 사용할 수 있지만, 5G 로 바꾼 후에는 C 를 사용할 수 있다. 이러한 공통 리스트는 QUIC 에서 완전히 암호화 되어있기 때문에, 잠재적인 공격자는 K 와 C 가 실제로 X 인지 알 수 없다.

댓글