HTTP(Hypertext Transfer Protocol)의 진화는 단순히 버전 번호의 나열이 아니라, 월드 와이드 웹(World Wide Web) 자체의 폭발적인 복잡성에 대한 직접적인 응답의 역사이다. HTTP 발전의 핵심 동력은 프로토콜의 본래 목적인 상태 비저장(stateless)의 단순한 설계와, 더 풍부한 미디어, 상호작용이 가능한 애플리케이션, 향상된 성능, 그리고 강력한 보안을 향한 웹의 끊임없는 요구 사이의 근본적인 긴장감이다.
HTTP의 역사는 '요청당 연결(connection-per-request)' 모델에서부터 TCP 프로토콜의 엄격한 제약에 이르기까지, 스스로 부과한 한계를 극복해 온 과정 그 자체라고 할 수 있다.
HTTP의 역사는 1989년 유럽 입자 물리 연구소(CERN)에서 팀 버너스리(Tim Berners-Lee)에 의해 시작되었다. 1991년에 문서화된 최초의 공식 버전인 HTTP/0.9는 월드 와이드 웹의 초기 비전을 실현하기 위한 극도로 단순한 목적을 가지고 있었다: 바로 하이퍼텍스트 문서를 간단한 클라이언트-서버 모델을 통해 가져오는 것이었다. 이 프로토콜은 700단어 미만의 평이한 문서로 요약될 만큼 미니멀리즘적인 설계를 반영했다.
HTTP/0.9는 '한 줄 프로토콜(one-line protocol)'이라는 별칭으로 불리기도 했는데, 이는 요청의 형태가 극도로 단순했기 때문이다. 클라이언트 요청은 단일 ASCII 라인으로 구성되었으며, 유일하게 가능한 메서드인 GET과 리소스 경로만으로 이루어졌다 (예: GET /my-page.html). 이 초기 버전에는 버전 번호, 헤더, 상태 코드, 메타데이터와 같은 현대적인 프로토콜의 요소가 전혀 존재하지 않았다.
서버의 응답 역시 요청만큼이나 단순했다. 서버는 요청된 HTML 콘텐츠의 원시 데이터(raw data)만을 전송했다. 요청의 성공 여부를 알릴 수 있는 표준화된 방법이 없었기 때문에, 만약 문제가 발생하면 서버는 단순히 연결을 닫아버렸고, 클라이언트는 그 원인을 추측할 수밖에 없었다. 이 버전은 오직 일반 텍스트(ASCII/HTML) 전송만 지원했으므로, 곧 웹의 상징이 될 이미지나 다른 미디어 유형을 처리하는 것은 불가능했다.
가장 중요한 특징 중 하나는 연결 모델이었다. 각 요청은 새로운 TCP 연결을 필요로 했으며, 이 연결은 응답이 전송된 직후 즉시 종료되었다. 이는 단일 문서를 검색하는 데는 효율적이었지만, 웹이 발전함에 따라 심각한 성능 병목 현상을 유발하는 원인이 되었다.
HTTP/1.0은 HTTP/0.9의 심각한 한계에 대한 직접적인 해결책으로 등장했다. 핵심적인 혁신은 HTTP 헤더의 도입이었다. 헤더는 메타데이터 전송을 가능하게 하여 프로토콜에 유연성과 확장성을 처음으로 부여했다.
GET /my-page.html HTTP/1.0), 기존 구현과의 호환성을 유지하면서 미래의 프로토콜 발전을 위한 기반이 마련되었다.HTTP/1.0 200 OK)으로 시작되었다. 이는 클라이언트가 요청의 결과를 표준화된 방식(200 OK, 404 Not Found 등)으로 이해할 수 있게 했다.Content-Type 헤더는 프로토콜이 HTML 외에 이미지, 오디오, 스크립트와 같은 다른 유형의 문서를 전송할 수 있게 하여 멀티미디어 웹 페이지의 시대를 열었다.이러한 중요한 개선에도 불구하고, HTTP/1.0은 여전히 비효율적인 '요청당 하나의 연결' 모델을 유지했다. 예를 들어, 10개의 이미지가 포함된 웹 페이지를 로드하기 위해서는 총 11개의 개별적인 TCP 연결을 설정하고 해제해야 했다. 이는 상당한 지연 시간과 서버 오버헤드를 유발했다.
[출처: https://mark-kim.blog/HTTP_0_9_to_1_1/]
HTTP의 초기 진화 과정은 단순한 기술적 업데이트를 넘어, 웹 플랫폼 자체의 본질적인 변화를 반영하는 철학적 전환이었다. HTTP/0.9에서 1.0으로의 전환은 웹이 단순한 하이퍼텍스트 시스템에서 멀티미디어 플랫폼으로 변모하는 결정적인 순간을 상징한다. HTTP/0.9는 오직 텍스트를 가져오는 단 하나의 목적을 위해 설계되었으며, 이는 학술적이고 텍스트 중심이었던 초기 웹의 특성과 완벽하게 부합했다.
그러나 웹 페이지에 이미지와 같은 미디어를 포함시키려는 욕구는 HTTP/0.9가 근본적으로 해결할 수 없는 문제를 야기했다. 브라우저에게 "이것은 JPEG 파일이다" 또는 "이것은 HTML이다"라고 구분하여 알려줄 방법이 없었기 때문이다. HTTP/1.0에서 Content-Type 헤더의 도입은 바로 이 콘텐츠 문제를 해결하기 위한 직접적인 기술적 해법이었다. 따라서 HTTP/1.0의 기능들(헤더, 상태 코드, 콘텐츠 유형)은 우리가 알고 있는 '멀티미디어 웹'을 가능하게 하기 위한 최소한의 필수 기능 집합으로 볼 수 있다. 즉, 프로토콜은 웹 플랫폼에 대한 창의적이고 상업적인 야망에 의해 진화하도록 강요받은 것이다.
동시에, 이 초기 프로토콜들의 연결 모델은 미래의 성능 문제의 씨앗을 뿌렸다. 당시에는 단순하고 논리적이었던 이 모델은 향후 20년 동안 웹 성능의 주요 병목 현상이 될 높은 오버헤드의 패턴을 확립했다. 1990년대 초반에는 네트워크 자원이 부족했고 단순성이 무엇보다 중요했다. '연결, 요청, 응답, 종료' 모델은 견고하고 구현하기 쉬웠다.
그러나 이 모델은 페이지의 리소스 수와 필요한 TCP 핸드셰이크 수를 직접적으로 연결시켰다. 페이지가 단일 HTML 문서에서 수십 개의 이미지, CSS 파일, 스크립트를 포함하는 형태로 성장함에 따라, 이 모델은 기하급수적으로 비효율적이 되었다. 각 핸드셰이크에 소요되는 지연 시간(완전한 왕복 시간)이 누적되어 페이지 로드 속도를 저하시켰다. HTTP/0.9와 1.0에서의 이 근본적인 설계 결정은 훗날 HTTP/1.1의 지속적 연결과 HTTP/2의 다중화가 해결하고자 했던 바로 그 문제를 만들어냈다. HTTP 성능 진화의 전체 경로는 이 초기의, 겉보기에는 사소했던 설계 결정의 직접적인 결과물인 셈이다.
HTTP/1.1의 가장 중요한 개선 사항은 지속적 연결을 기본값으로 채택한 것이다. 이는 여러 요청과 응답을 단일 TCP 연결을 통해 전송할 수 있게 하여, 반복적인 연결 설정으로 인한 지연 시간을 극적으로 줄였다. 이는 HTTP/1.0의 주된 비효율성을 직접적으로 해결한 것이었다.
[출처: https://mark-kim.blog/HTTP_0_9_to_1_1/]
이 버전부터 Host 헤더가 필수가 되었다. 이 헤더는 단일 IP 주소를 가진 서버가 여러 웹사이트(가상 호스팅)를 호스팅할 수 있도록 허용하여, 웹의 폭발적인 성장과 웹 호스팅의 경제성에 결정적인 역할을 했다.
[출처:https://www.geeksforgeeks.org/web-tech/http-headers-host/]
Cache-Control 및 ETag와 같은 더 정교한 캐시 제어 헤더를 도입하여, 개발자들이 캐싱 동작을 더 세밀하게 제어하고 불필요한 요청을 줄일 수 있도록 했다.
서버가 콘텐츠의 전체 크기를 미리 알지 못해도 클라이언트에게 응답을 스트리밍할 수 있게 하여, 동적으로 생성되는 콘텐츠 전송에 필수적인 기능이 되었다.
[출처: https://bunny.net/academy/http/what-is-chunked-encoding/]
PUT, DELETE, OPTIONS, TRACE, CONNECT와 같은 메서드가 추가되어, 더 복잡하고 RESTful한 웹 애플리케이션 및 API 개발을 가능하게 했다.
HTTP/1.1의 선택적 기능인 파이프라이닝(Pipelining)은 클라이언트가 단일 지속 연결을 통해 응답을 기다리지 않고 여러 요청을 보낼 수 있도록 허용했다. 이론적으로 이는 네트워크 유휴 시간을 줄이고 특히 지연 시간이 긴 네트워크에서 성능을 향상시킬 수 있었다.
[출처: https://en.wikipedia.org/wiki/HTTP_pipelining]
그러나 명세는 서버가 요청을 받은 순서와 정확히 동일한 순서로 응답을 보내야 한다고 규정했다.
이 엄격한 순서 요구 사항은 새로운 문제를 야기했다. 만약 파이프라인의 첫 번째 요청(예: 복잡한 데이터베이스 쿼리) 처리가 느려지면, 서버가 이미 전송 준비를 마친 후속 응답들(예: 작은 이미지 파일)까지 모두 지연되는 현상이 발생했다. 전체 연결이 '줄의 맨 앞(head of the line)'에 의해 막히게 된 것이다.
[출처: https://dev-ws.tistory.com/124]
HOL 블로킹 문제와 프록시 및 서버에서의 일관성 없고 버그가 많은 구현으로 인해, 파이프라이닝은 신뢰성이 매우 낮았다. 결과적으로 대부분의 브라우저에서 기본적으로 비활성화되었고, 널리 효과적으로 채택되지 못했다.
HOL 블로킹과 브라우저가 도메인당 부과하는 병렬 연결 수 제한(보통 6개)을 모두 우회하기 위해, 개발자들은 도메인 샤딩(Domain Sharding)과 같은 편법에 의존했다. 이는 자산을 여러 하위 도메인(예: img1.site.com, img2.site.com)에 분산시켜 브라우저가 더 많은 병렬 TCP 연결을 열도록 유도하는 방식이었다.
[출처: https://blog.cloudflare.com/http-2-for-web-developers/]
HTTP/1.1은 TCP 기반의 텍스트 기반, 순차적 요청-응답 패러다임을 최적화의 '지역 최댓값(local maximum)'까지 밀어붙인 프로토콜이었다. 그 기능들은 영리한 최적화였지만, 근본적인 아키텍처의 제약을 극복하지는 못했다. HTTP/1.1의 주요 기능들—지속적 연결, 캐싱, 청크 인코딩—은 모두 기존 모델을 더 효율적으로 만드는 데 초점을 맞추고 있다. 이들은 오버헤드를 줄이지만 프로토콜의 핵심적인 순차적 특성을 바꾸지는 않는다.
파이프라이닝은 이 순차 모델을 깨뜨리려는 시도였지만, 재설계가 아닌 임시방편에 불과했다. 이는 엄격하게 순서가 정해진 전송 계층(TCP)과 자체적으로 부과한 FIFO 규칙에 제약을 받으면서 애플리케이션 계층에서 병렬성을 도입하려 했다. 파이프라이닝의 실패는 HTTP/1.1 프레임워크 내에서는 진정한 병렬성을 달성할 수 없음을 명백히 보여주었다. 애플리케이션 계층의 HOL 블로킹은 극복할 수 없는 장벽이었다. 따라서, 더 이상의 의미 있는 성능 향상을 위해서는 텍스트 기반의 순차 모델에서 완전히 벗어나는 것이 필요했다. 이러한 깨달음은 HTTP/2라는 근본적인 재설계로 직접 이어졌다. HTTP/1.1은 당시로서는 최선이었지만, 그 '최선'은 더 이상 현대 웹의 요구를 충족시키기에 충분하지 않았다.
더 나아가, 개발자들의 관행은 프로토콜의 한계를 '해킹'하는 방향으로 진화했다. 이미지 스프라이팅, 인라이닝, 파일 병합, 도메인 샤딩과 같은 성능 최적화 기법의 유행은 단순한 '모범 사례'가 아니었다. 이는 프로토콜의 실패를 보여주는 직접적이고 구체적인 증거였다. HTTP/1.1의 핵심 문제는 각 개별 요청에 따르는 높은 오버헤드였다. CSS/JS 파일을 병합하고 이미지 스프라이트를 사용하는 것은 요청의 수를 줄이기 위해 고안된 기법들이다. 도메인 샤딩은 도메인당 약 6개로 제한된 브라우저의 병렬 연결 수를 늘리기 위한 기법이었다.
이러한 기법들은 개발 워크플로우를 복잡하게 만들며, HTTP/2 환경에서는 오히려 안티 패턴이 될 수 있다. HTTP/1.1 시대에 이러한 기법들이 존재하고 인기를 끌었다는 사실 자체가, 다수의 작은 리소스를 효율적으로 처리하지 못하는 프로토콜의 무능력을 직접적으로 드러내는 증상이다. 본질적으로, 이는 망가진 모델에 대한 임시방편이었던 것이다.
HTTP/2는 텍스트 기반이 아닌 바이너리 프로토콜이다. 이는 기계가 파싱하기에 더 간결하고 효율적이며, 공백, 대소문자 등으로 인한 오류 발생 가능성이 적다. HTTP/1.1처럼 수동으로 읽거나 생성할 수 없다.
[출처: https://hpbn.co/http2/]
핵심 개념은 통신을 바이너리 프레이밍 계층으로 분해하는 것이다. 단일 TCP 연결은 여러 개의 양방향 스트림(streams)을 전달한다. 각 스트림은 메시지(messages)(요청 또는 응답)를 운반하며, 이 메시지들은 더 작은 단위인 프레임(frames)으로 나뉜다. 이 추상화는 HTTP/2의 모든 주요 기능의 핵심이다.
[출처: https://hpbn.co/http2/]
HTTP/2는 진정한 다중화(Multiplexing)를 도입했다. 여러 요청과 응답이 단일 TCP 연결을 통해 동시에 전송될 수 있으며, 이들의 프레임은 서로 뒤섞여(interleaved) 전달된다. 클라이언트와 서버는 각 스트림에 해당하는 프레임들을 독립적으로 재조립한다.
[출처: https://hpbn.co/http2/]
이는 HTTP/1.1 파이프라이닝을 괴롭혔던 HOL 블로킹 문제를 완전히 해결한다. 하나의 스트림(예: /api/data)에 대한 느린 응답이 더 이상 다른 준비된 응답(예: /css/style.css)을 막지 않는다.
HTTP/2는 클라이언트가 각 스트림에 가중치와 의존성을 부여하여 서버에게 어떤 리소스가 더 중요한지(예: 렌더링을 막는 CSS를 푸터 이미지보다 먼저 로드) 신호를 보낼 수 있게 한다. 이는 사용자가 인지하는 로딩 경험을 세밀하게 제어할 수 있는 강력한 기능을 제공한다.
HTTP/1.1에서는 많은 헤더(예: User-Agent, Accept)가 모든 요청마다 반복적으로 전송되었다. HPACK은 정적 허프만 코드와 이전에 전송된 헤더의 동적 테이블을 사용하여 이 중복 데이터를 극적으로 줄이는, HTTP 헤더에 특화된 고도로 최적화된 압축 형식이다. 이를 통해 대역폭을 절약하고 지연 시간을 단축한다.
[출처: https://hpbn.co/http2/]
서버는 클라이언트가 명시적으로 요청하기 전에 필요할 것으로 예상되는 리소스를 사전에 "푸시"할 수 있다. 예를 들어, 클라이언트가 index.html을 요청할 때 서버는 style.css와 script.js를 함께 푸시하여, 클라이언트가 HTML을 파싱한 후 해당 자산을 다시 요청하는 데 드는 왕복 시간을 절약할 수 있다.
[출처: https://hpbn.co/http2/]
HTTP/2 명세 자체는 암호화되지 않은 사용(h2c)을 허용하지만, 모든 주요 브라우저 공급업체(Chrome, Firefox, Safari, Edge)는 암호화된 TLS 연결(h2)을 통해서만 HTTP/2를 구현하기로 정책적으로 결정했다. 이로 인해 HTTPS는 HTTP/2의 성능 이점을 얻기 위한 실질적인 전제 조건이 되었다.
과거에는 TLS 핸드셰이크가 추가적인 지연 시간을 유발하여 HTTPS가 HTTP보다 느리다는 주장이 있었다. 그러나 이 주장은 이제 두 가지 이유로 유효하지 않다:
브라우저 공급업체들은 웹에 대해 '성능을 위한 보안'이라는 '거대한 타협(Grand Bargain)'을 강요했다. HTTP/2를 TLS에 연결하기로 한 결정은 단순한 기술적 선택이 아닌, 웹 생태계의 방향을 바꾼 중대한 정책적 움직임이었다. 이는 웹사이트 운영자들이 HTTPS로 이전하도록 하는 강력하고 협상 불가능한 인센티브를 만들어냈다.
수년 동안 보안 전문가들은 HTTPS 채택을 주장했지만, 이는 종종 사용자에게 직접적인 혜택이 보이지 않는 비용으로 인식되어 제한적인 성공을 거두었다. 브라우저 공급업체들은 웹 플랫폼을 보호하기 위한 노력의 일환으로, 지리적 위치 정보(Geolocation), getUserMedia와 같은 강력한 새 API들을 보안 컨텍스트(HTTPS)에서만 사용 가능하도록 제한하기 시작했다. 그들은 이와 동일한 철학을 HTTP/2에도 적용했다. 지난 15년 만의 가장 큰 웹 성능 향상을 HTTPS를 통해서만 제공함으로써, 그들은 암호화를 '있으면 좋은' 보안 기능에서 성능을 위해 '반드시 있어야 하는' 기능으로 전환시켰다. 이는 사실상 논쟁을 종식시켰다. 빠른 웹사이트를 원한다면 HTTPS를 사용해야 한다는 비즈니스 케이스가 명확해졌기 때문이다. 이 정책적 결정은 아마도 대규모 HTTPS 채택을 촉발한 가장 효과적인 단일 촉매제였을 것이다.
또한, HTTP/2는 기존의 모범 사례들을 뒤집고 개발을 단순화했다. 이 프로토콜은 HTTP/1.1의 문제들을 매우 효과적으로 해결했기 때문에, 오랫동안 유지되어 온 많은 성능 '해킹'들을 쓸모없게 만들거나 심지어 해롭게 만들었다. 파일 병합이나 스프라이팅과 같은 HTTP/1.1의 해결책들은 요청 수를 줄이기 위해 고안되었다. 반면, HTTP/2의 다중화와 헤더 압축은 많은 작은 요청들을 매우 효율적으로 처리한다.
따라서 HTTP/2 환경에서 모든 CSS를 하나의 거대한 파일로 병합하는 것은 안티 패턴이 될 수 있다. 이는 캐싱을 깨뜨릴 수 있고(작은 변경만으로 전체 파일을 다시 다운로드해야 함), 브라우저가 더 작고 중요한 CSS 파일들이 도착함에 따라 점진적으로 페이지를 렌더링하는 것을 방해할 수 있다. 마찬가지로, HTTP/2는 고도로 최적화된 단일 연결을 사용하도록 설계되었기 때문에 도메인 샤딩은 완전히 불필요하고 비생산적이다. 이는 프론트엔드 개발 및 배포 파이프라인의 주요한 단순화를 의미한다. 이제 개발자들은 전송 프로토콜의 한계에 맞춰 코드를 구성하는 대신, 논리적인 코드 구성에 더 집중할 수 있게 되었다.
사용자를 http://example.com에서 https://example.com으로 리디렉션하는 것은 일반적인 설정이다. 그러나 이 최초의 HTTP 요청은 암호화되지 않았기 때문에 중간자 공격(Man-in-the-Middle, MITM)에 취약하다.
공격자는 이 최초의 HTTP 요청을 가로채 사용자에게는 일반 HTTP를 통한 프록시 연결을 제공하면서, 자신은 서버와 HTTPS로 통신할 수 있다. 사용자의 브라우저는 리디렉션을 인지하지 못하고 암호화되지 않은 텍스트로 통신하게 되어 SSL/TLS의 목적을 무력화시킨다. 이는 프로토콜 다운그레이드 공격의 한 형태이다.
웹사이트는 Strict-Transport-Security 응답 헤더를 전송하여 HSTS를 선택한다. 이 헤더는 보안(HTTPS) 연결을 통해서 전송될 때만 효력이 있다.
브라우저가 이 헤더를 수신하면, 해당 도메인을 지정된 기간 동안 내부 HSTS 목록에 추가한다. 이 기간 동안 해당 도메인에 대한 향후 모든 방문 시, 브라우저는 네트워크를 통해 요청을 보내기 전에 모든 http:// 요청을 https://로 자동 업그레이드한다. 이는 SSL 스트리핑 공격의 기회를 원천적으로 차단한다.
max-age=<seconds>: (필수) 브라우저가 HTTPS를 강제해야 하는 기간을 초 단위로 지정한다. 일반적으로 1년 또는 2년의 값이 권장된다.includeSubDomains: (선택 사항이지만 권장) 이 지시어가 있으면 HSTS 정책이 해당 도메인의 모든 하위 도메인에도 적용된다. 이는 보안되지 않은 하위 도메인의 취약점이 상위 도메인의 쿠키를 손상시키는 공격을 방지하는 데 매우 중요하다.preload: (선택 사항) 사이트 소유자가 브라우저의 하드코딩된 HSTS 사전 로드 목록(preload list)에 포함되는 데 동의한다는 신호이다.HSTS는 브라우저가 성공적인 HTTPS 연결을 한 번 이상 설정하고 헤더를 수신한 이후에만 효과적이다. 따라서 사이트에 대한 첫 방문은 여전히 취약하다.
이 문제를 해결하기 위해 브라우저 공급업체들은 HSTS가 활성화된 것으로 하드코딩된 도메인 목록인 '사전 로드 목록(preload list)'을 유지 관리한다. 이 목록에 있는 도메인의 경우, 브라우저는 첫 방문부터 HTTPS를 강제하여 최고 수준의 보호를 제공한다.
목록에 포함되려면 사이트는 유효한 인증서를 제공하고, HTTP를 HTTPS로 리디렉션하며, 모든 하위 도메인을 HTTPS로 서비스하고, 긴 max-age, includeSubDomains, preload 지시어를 포함한 HSTS 헤더를 전송해야 한다.
HSTS는 보안 모델을 '기회주의적' 보안에서 '결정론적' 보안으로 전환시켰다. 이는 "가능하다면 HTTPS를 사용하겠다"는 접근 방식에서 "이 도메인에 대해서는 오직 HTTPS만 사용하고, 그렇지 않으면 연결을 거부하겠다"는 방식으로의 근본적인 변화를 의미한다.
표준적인 HTTP-to-HTTPS 리디렉션은 기회주의적이다. 클라이언트는 비보안 요청을 하고 보안 요청으로 리디렉션되기를 희망한다. 이 희망은 능동적인 공격자에 의해 좌절될 수 있다. 반면, HSTS는 일단 브라우저에 캐시되면 이러한 모호성을 제거한다. 브라우저의 정책은 결정론적이 된다: 도메인 X에 대해 프로토콜은 반드시 HTTPS여야 한다. 또한 사용자가 인증서 경고를 무시하고 진행할 수 있는 기능을 제거하여 보안 태세를 더욱 강화한다. 이는 클라이언트가 서버와 맺는 보안 계약의 근본적인 변화이며, 단순한 제안에서 클라이언트 자체가 강제하는 엄격하고 협상 불가능한 규칙으로의 전환이다.
이러한 맥락에서 preload 목록은 분산된 신뢰 문제에 대한 중앙 집중식 해결책이다. 이는 HSTS 설계에 내재된 '최초 사용 시 신뢰' 취약점에 대한 실용적이지만 완벽하지는 않은 보완책이다. HSTS의 핵심 약점은 브라우저가 첫 방문 시 정책을 '학습'해야 한다는 점이며, 이 첫 방문 자체가 손상될 수 있다는 것이다.
분산된 해결책으로는 HSTS 정책을 선언하기 위해 DNSSEC 레코드를 사용하는 것이 있을 수 있지만, 이는 자체적인 복잡성과 채택의 어려움을 가진다. 구글이 유지 관리하고 다른 브라우저들이 공유하는 사전 로드 목록은 매우 효과적인 중앙 집중식 접근 방식이다. 이는 신뢰망(web-of-trust) 역할을 하지만, 브라우저 공급업체가 중앙 권위 기관이 되는 형태이다. 여기에는 중요한 함의가 있다. 목록에 등재되려면 엄격한 기준을 충족해야 하며, 목록에서 제거되는 과정은 느리다. 이는 보안을 위해 중요한 웹 인프라 기능에 대한 통제권이 브라우저 제조업체에게 점차 중앙 집중화되는 경향을 보여주며, 이들이 사실상의 게이트키퍼 역할을 하고 있음을 시사한다.
HTTP/2가 애플리케이션 계층의 HOL 블로킹을 해결했지만, 이는 TCP에 내재된 더 깊고 근본적인 문제인 전송 계층(transport-layer) HOL 블로킹을 드러냈다.
TCP는 신뢰성 있고 순서가 보장되는 바이트 스트림이다. 만약 단일 TCP 패킷이 전송 중에 손실되면, 비록 수신되었고 다른 HTTP/2 스트림에 속해 있더라도 모든 후속 패킷들은 손실된 패킷이 재전송되어 도착할 때까지 운영 체제의 버퍼에서 대기해야 한다. 이는 단 하나의 스트림이 아닌, 다중화된 모든 스트림을 지연시킨다.
[출처: https://engineering.cred.club/head-of-line-hol-blocking-in-http-1-and-http-2-50b24e9e3372]
HTTP/3는 TCP를 버리고 UDP 위에서 실행되는 새로운 전송 프로토콜인 QUIC(Quick UDP Internet Connections)을 기반으로 구축되었다.
UDP는 순서나 전달을 보장하지 않는 "발사 후 망각(fire-and-forget)" 프로토콜이다. 이는 후퇴처럼 들릴 수 있지만, QUIC에게 TCP의 제약에서 벗어나 신뢰성, 혼잡 제어와 같은 전송 계층 로직을 더 지능적인 방식으로 재구현할 수 있는 백지 상태를 제공한다.
단일 바이트 스트림을 사용하는 TCP와 달리, QUIC은 전송 계층에서 여러 개의 독립적인 스트림을 기본적으로 지원한다. 한 스트림의 데이터를 운반하는 패킷이 손실되더라도, 이는 해당 특정 스트림에만 영향을 미친다. 다른 스트림들은 대기 없이 계속 처리될 수 있으므로, 전송 계층 HOL 블로킹을 완전히 제거한다.
[출처: https://www.cdnetworks.com/ko/blog/what-is-quic/]
QUIC은 암호화 핸드셰이크(TLS 1.3 사용)를 전송 핸드셰이크와 통합한다. 새로운 연결은 단일 왕복 시간(1-RTT) 내에 설정될 수 있다. 결정적으로, 알려진 서버에 대한 후속 연결의 경우, 클라이언트는 첫 번째 패킷에 애플리케이션 데이터를 담아 보낼 수 있어(0-RTT, Zero Round-Trip Time), 연결 재개를 거의 즉각적으로 만든다.
[출처: https://www.cdnetworks.com/ko/blog/what-is-quic/]
QUIC에서 암호화는 부가 기능이 아니라 프로토콜의 근본적인 부분이다. 페이로드뿐만 아니라 많은 메타데이터도 암호화되어 개인 정보 보호를 강화하고, 네트워크 중간 장비(middlebox)에 의한 프로토콜 경직화(ossification)에 더 강해진다.
TCP 연결은 4-튜플(소스 IP, 소스 포트, 목적지 IP, 목적지 포트)로 정의된다. 이 중 하나라도 변경되면(예: 사용자의 휴대폰이 Wi-Fi에서 셀룰러로 전환) TCP 연결은 끊어진다. QUIC은 고유한 연결 ID(Connection ID, CID)를 사용하여 연결을 식별한다. 이를 통해 클라이언트는 처음부터 연결을 다시 설정할 필요 없이 네트워크를 원활하게 전환할 수 있어, 모바일 사용자에게 큰 이점을 제공한다.
[출처: https://www.cdnetworks.com/ko/blog/what-is-quic/]
HPACK과 유사하지만 QUIC의 순서 없는 스트림 전달 방식에 맞게 조정된 새로운 헤더 압축 형식이다.
| 기능 | HTTP/0.9 | HTTP/1.0 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|---|---|
| 프로토콜 형식 | 텍스트 | 텍스트 | 텍스트 | 바이너리 | 바이너리 |
| 연결 모델 | 요청당 1개 | 요청당 1개 | 지속적 연결 | 단일 연결 | 단일 연결 |
| 병렬성 | 없음 | 없음 | 파이프라이닝 (결함) | 다중화 | 다중화 |
| HOL 블로킹 | 해당 없음 | 해당 없음 | 애플리케이션 계층 | TCP 계층 | 제거됨 |
| 헤더 압축 | 없음 | 없음 | 없음 | HPACK | QPACK |
| 서버 푸시 | 아니요 | 아니요 | 아니요 | 예 | 예 |
| 암호화 | 없음 | 없음 | 선택 사항 (HTTPS) | 사실상 필수 | 필수 |
| 전송 프로토콜 | TCP | TCP | TCP | TCP | QUIC (UDP) |
HTTP/3는 성능 향상을 위해 복잡성을 스택의 하위 계층으로 이동시키는 최종 단계를 나타낸다. HTTP의 진화는 성능 병목 현상을 식별하고 그 해결책을 전송 계층에 더 가깝게 밀어붙이는 명확한 경향을 보여준다. HTTP/1.1은 파이프라이닝을 통해 애플리케이션 계층에서 병렬성을 해결하려 했지만 HOL 블로킹으로 인해 실패했다. HTTP/2는 HTTP 자체 내에 새로운 프레이밍 및 다중화 하위 계층을 도입하여 애플리케이션 계층 HOL 블로킹을 해결했지만, 여전히 기본 전송 계층(TCP)에 의해 제한되었다.
HTTP/3의 해결책은 가장 급진적이다. TCP를 우회하는 대신, 다중화가 기본적이고 일급 개념인 새로운 UDP 기반 전송 프로토콜(QUIC)로 완전히 대체한다. 이는 문제 해결의 진행 과정을 보여준다: 애플리케이션 계층의 임시방편(파이프라이닝)에서, 애플리케이션 계층의 재설계(HTTP/2 다중화), 그리고 완전한 전송 계층의 교체(QUIC)에 이르기까지, 문제는 근본적인 원인이 있는 곳까지 스택 아래로 밀려 내려가 해결되었다.
더불어, QUIC의 설계는 프로토콜 경직화(protocol ossification)에 대한 선제적 대응이다. QUIC을 UDP 기반으로 구축한 주된, 그러나 잘 알려지지 않은 이유는 네트워크 하드웨어(중간 장비, 방화벽)가 미래의 프로토콜 진화를 방해하는 것을 막기 위함이었다. TCP는 운영 체제 커널과 네트워크 하드웨어에 깊숙이 내장되어 있어 진화가 매우 느리고 어렵다. 새로운 TCP 옵션은 종종 중간 장비에 의해 오해받고 폐기되는데, 이를 프로토콜 경직화 현상이라고 한다.
반면, 더 단순한 UDP는 일반적으로 이러한 장비들을 간섭 없이 통과한다. QUIC의 로직을 사용자 공간(즉, 브라우저/애플리케이션 코드)에 구축하고 UDP 위에서 실행함으로써, 개발자들은 애플리케이션을 업데이트하는 것만큼 쉽게 전송 프로토콜의 새 버전을 반복하고 배포할 수 있다. 또한, 프로토콜 메타데이터의 대부분을 암호화함으로써 QUIC은 자신을 중간 장비에 불투명하게 만들어, 그들이 메커니즘을 검사하고 잠재적으로 간섭하는 것을 방지한다. 이는 TCP의 수십 년간의 정체에서 얻은 교훈을 바탕으로, 프로토콜이 미래에도 계속 진화할 수 있도록 보장하기 위한 의도적이고 전략적인 설계 선택이다.