HTTP/2 소개

HTTP/2를 사용하면 애플리케이션 내에서 이전에 수행된 여러 HTTP/1.1 해결 방법을 실행 취소하고 전송 계층 자체 내에서 이러한 문제를 해결할 수 있으므로 애플리케이션을 더 빠르고 단순하며 견고하게 만들 수 있습니다(드문 경우임). 게다가 애플리케이션을 최적화하고 성능을 개선할 수 있는 완전히 새로운 기회를 활용할 수도 있습니다.

HTTP/2의 주요 목표는 전체 요청 및 응답 다중화를 지원하여 지연 시간을 줄이고, HTTP 헤더 필드의 효율적인 압축을 통해 프로토콜 오버헤드를 최소화하며, 요청 우선순위 지정 및 서버 푸시를 지원하는 것입니다. 이러한 요구사항을 구현하기 위해 새로운 흐름 제어, 오류 처리, 업그레이드 메커니즘과 같은 기타 프로토콜 개선사항을 다양하게 지원하고 있지만 모든 웹 개발자가 이해하고 애플리케이션에서 활용해야 하는 가장 중요한 기능입니다.

HTTP/2는 어떤 방식으로도 HTTP의 애플리케이션 의미 체계를 수정하지 않습니다. HTTP 메서드, 상태 코드, URI, 헤더 필드와 같은 모든 핵심 개념은 그대로 유지됩니다. 대신 HTTP/2는 전체 프로세스를 관리하는 클라이언트와 서버 간에 데이터의 형식 지정 (프레임) 및 전송 방식을 수정하고 애플리케이션의 모든 복잡성을 새로운 프레이밍 레이어 내에 숨깁니다. 따라서 기존의 모든 애플리케이션을 수정 없이 제공할 수 있습니다.

HTTP/1.2가 아닌 이유는 무엇인가요?

HTTP Working Group에서 설정한 성능 목표를 달성하기 위해 HTTP/2에서는 이전 HTTP/1.x 서버 및 클라이언트와 하위 호환되지 않는 새로운 바이너리 프레이밍 레이어를 도입합니다. 따라서 주 프로토콜 버전이 HTTP/2로 증가합니다.

즉, 원시 TCP 소켓을 사용하여 웹 서버 (또는 커스텀 클라이언트)를 구현하지 않는 한 아무런 차이가 없습니다. 모든 새로운 하위 수준 프레이밍은 클라이언트와 서버에서 자동으로 수행됩니다. 눈에 띄는 유일한 차이점은 성능 개선과 요청 우선순위 지정, 흐름 제어, 서버 푸시와 같은 새로운 기능의 가용성입니다.

SPDY 및 HTTP/2의 간략한 역사

SPDY는 Google에서 개발하여 2009년 중반에 발표된 실험용 프로토콜로, HTTP/1.1의 잘 알려진 성능 제한 중 일부를 해결하여 웹페이지의 로드 지연 시간을 줄이는 것이 주요 목표였습니다. 구체적으로 기술된 프로젝트 목표는 다음과 같이 설정되었습니다.

  • 페이지 로드 시간 (PLT)을 50% 줄입니다.
  • 웹사이트 작성자가 콘텐츠를 변경할 필요가 없도록 합니다.
  • 배포 복잡성을 최소화하고 네트워크 인프라를 변경하지 않습니다.
  • 오픈소스 커뮤니티와 협력하여 이 새로운 프로토콜을 개발합니다.
  • 실험 프로토콜을 (무효화)하기 위해 실제 성능 데이터를 수집합니다.

초기 발표 후 얼마 지나지 않아 Google의 소프트웨어 엔지니어인 마이크 벨셰와 로베르토 페온이 새로운 SPDY 프로토콜의 실험용 구현에 대한 첫 번째 결과, 문서 및 소스 코드를 공유했습니다.

지금까지는 실험실 조건에서만 SPDY를 테스트했습니다. 초기 결과는 매우 고무적입니다. 시뮬레이션된 홈 네트워크 연결을 통해 상위 25개 웹사이트를 다운로드하면 성능이 크게 개선되어 페이지 로드 속도가 최대 55% 빨라집니다. (Chromium 블로그)

2012년부터는 새로운 실험용 프로토콜이 Chrome, Firefox, Opera에서 지원되었으며, 인프라 내에 SPDY를 배포하는 대규모 (예: Google, Twitter, Facebook) 및 소규모 사이트의 수가 급격히 증가하고 있습니다. 실제로 SPDY는 업계의 채택률이 늘어남에 따라 실질적인 표준이 되어 가고 있었습니다.

HTTP Working Group (HTTP-WG)은 이러한 트렌드를 관찰하면서 SPDY에서 얻은 교훈을 바탕으로 빌드 및 개선하여 공식 'HTTP/2' 표준을 제공하기 위한 새로운 노력을 시작했습니다. 새로운 헌장이 작성되었고 HTTP/2 제안에 대한 공개 요청이 있었으며 실무 그룹 내에서 많은 논의를 거쳐 새 HTTP/2 프로토콜의 출발점으로 SPDY 사양이 채택되었습니다.

그 후 몇 년 동안 SPDY와 HTTP/2는 동시에 계속 발전했으며, SPDY는 HTTP/2 표준의 새로운 기능과 제안을 테스트하는 데 사용되는 실험용 브랜치로 기능했습니다. 이론적으로 유효해 보이는 것이 실제로 작동하지 않을 수 있고 그 반대의 경우도 마찬가지입니다. SPDY는 각 제안을 HTTP/2 표준에 포함하기 전에 테스트하고 평가할 수 있는 경로를 제공했습니다. 결국 이 프로세스는 3년 동안 진행되었으며 12개가 넘는 중간 초안으로 이어졌습니다.

  • 2012년 3월: HTTP/2 제안 요청
  • 2012년 11월: HTTP/2의 첫 번째 초안 (SPDY 기반)
  • 2014년 8월: HTTP/2 초안-17 및 HPACK 초안-12 게시
  • 2014년 8월: 실무 그룹의 HTTP/2 최종 통화
  • 2015년 2월: IESG가 HTTP/2 및 HPACK 초안을 승인
  • 2015년 5월: RFC 7540 (HTTP/2) 및 RFC 7541 (HPACK) 게시

2015년 초에 IESG가 새로운 HTTP/2 표준을 검토하고 발행을 승인했습니다. 그 직후 Chrome팀은 TLS의 SPDY 및 NPN 확장에 대한 지원을 중단하는 일정을 발표했습니다.

HTTP/1.1에서 HTTP/2의 주요 변경사항은 향상된 성능에 초점을 맞춥니다. 다중화, 헤더 압축, 우선순위 지정, 프로토콜 협상과 같은 일부 주요 기능은 이전에 개방되었지만 표준이 아닌 프로토콜인 SPDY에서 수행된 작업에서 발전했습니다. Chrome은 Chrome 6부터 SPDY를 지원했지만 대부분의 혜택이 HTTP/2에 있으므로 이제 지원이 중단됩니다. Google은 2016년 초에 SPDY에 대한 지원을 중단하고 동시에 Chrome의 ALPN 대신 TLS 확장 프로그램인 NPN에 대한 지원을 중단할 계획입니다. 서버 개발자는 HTTP/2 및 ALPN으로 전환하는 것이 좋습니다.

Google은 HTTP/2로 이어지게 된 개방형 표준 프로세스에 기여하게 되어 기쁘게 생각하며, 표준화 및 구현에 대한 업계 전반의 참여를 고려할 때 HTTP/2가 널리 채택되기를 바랍니다. (Chromium 블로그)

SPDY 및 HTTP/2가 함께 발전한 덕분에 서버, 브라우저 및 사이트 개발자는 개발 중인 새로운 프로토콜을 실제로 사용해 볼 수 있었습니다. 따라서 HTTP/2 표준은 가장 광범위하게 테스트를 거친 가장 우수한 표준 중 하나입니다. HTTP/2가 IESG에서 승인될 무렵에는 철저하게 테스트되어 프로덕션에 즉시 사용 가능한 수십 개의 클라이언트 및 서버 구현이 있었습니다. 실제로 최종 프로토콜이 승인된 지 불과 몇 주 만에 여러 인기 브라우저와 많은 사이트가 HTTP/2를 완벽하게 지원했기 때문에 많은 사용자가 이미 프로토콜의 이점을 누리고 있었습니다.

설계 및 기술 목표

이전 버전의 HTTP 프로토콜은 의도적으로 간단히 구현하기 위해 설계되었습니다. HTTP/0.9는 월드 와이드 웹을 부트스트랩하기 위한 한 줄 프로토콜이었고, HTTP/1.0은 HTTP/0.9에 대한 인기 있는 확장 프로그램을 정보 표준에 문서화했으며, HTTP/1.1에는 공식 IETF 표준이 도입되었습니다. HTTP의 간략한 역사를 참조하세요. 따라서 HTTP/0.9-1.x는 의도했던 대로 정확히 전달했습니다. HTTP는 인터넷에서 가장 널리 채택된 애플리케이션 프로토콜 중 하나입니다.

불행히도 구현 단순성에는 애플리케이션 성능이 희생되었습니다. HTTP/1.x 클라이언트는 동시 실행을 달성하고 지연 시간을 줄이기 위해 여러 연결을 사용해야 합니다. HTTP/1.x는 요청 및 응답 헤더를 압축하지 않으므로 불필요한 네트워크 트래픽이 발생합니다. HTTP/1.x는 효율적인 리소스 우선순위를 지정하지 못하므로 기본 TCP 연결을 제대로 사용하지 못하는 등의 문제가 있습니다.

이러한 한계가 치명적이지는 않았지만, 일상생활에서 웹 애플리케이션의 범위, 복잡성, 중요성이 계속 증가하면서 웹의 개발자와 사용자 모두에게 점점 더 큰 부담이 되었습니다. 이것이 바로 HTTP/2가 해결하고자 하는 바로 그 격차입니다.

HTTP/2를 사용하면 헤더 필드 압축을 도입하고 동일한 연결에서 여러 동시 교환을 허용하여 네트워크 리소스를 보다 효율적으로 사용하고 지연 시간에 대한 인식을 줄일 수 있습니다. 특히 동일한 연결에서 요청 및 응답 메시지의 인터리브가 가능하며 HTTP 헤더 필드에 효율적인 코딩을 사용합니다. 또한 요청의 우선순위를 지정하여 더 중요한 요청이 더 빨리 완료되도록 하여 성능을 더욱 향상시킬 수 있습니다.

결과로 얻은 프로토콜은 HTTP/1.x에 비해 사용할 수 있는 TCP 연결이 더 적기 때문에 네트워크에 더 친화적입니다. 즉, 다른 흐름과의 경쟁이 적고 연결 수명이 길어져 사용 가능한 네트워크 용량의 활용도가 향상됩니다. 마지막으로 HTTP/2에서는 바이너리 메시지 프레이밍을 사용하여 보다 효율적으로 메시지를 처리할 수도 있습니다. (Hypertext Transfer Protocol 버전 2, 초안 17)

HTTP/2는 이전의 HTTP 표준을 대체하는 것이 아니라 확장한다는 점에 유의해야 합니다. HTTP의 애플리케이션 시맨틱스는 동일하며 제공되는 기능이나 핵심 개념(예: HTTP 메서드, 상태 코드, URI, 헤더 필드)은 변경되지 않습니다. 이러한 변경은 HTTP/2 작업의 범위를 명시적으로 벗어났습니다. 즉, 상위 수준 API는 동일하게 유지되지만 하위 수준의 변경사항이 이전 프로토콜의 성능 제한을 어떻게 해결하는지 이해하는 것이 중요합니다. 바이너리 프레이밍 레이어와 그 기능을 간단하게 살펴보겠습니다

바이너리 프레이밍 레이어

HTTP/2의 모든 성능 향상의 핵심에는 HTTP 메시지가 캡슐화되고 클라이언트와 서버 간에 전송되는 방식을 결정하는 새로운 바이너리 프레이밍 레이어가 있습니다.

HTTP/2 바이너리 프레이밍 레이어

'레이어'는 애플리케이션에 노출되는 소켓 인터페이스와 상위 HTTP API 간에 새롭게 최적화된 인코딩 메커니즘을 도입하기 위한 설계 옵션을 나타냅니다. HTTP 시맨틱스(예: 동사, 메서드, 헤더)는 영향을 받지 않지만 전송 중에 인코딩되는 방식은 다릅니다. 줄바꿈으로 구분된 일반 텍스트 HTTP/1.x 프로토콜과 달리 모든 HTTP/2 통신은 작은 메시지와 프레임으로 분할되며 각각은 바이너리 형식으로 인코딩됩니다.

따라서 클라이언트와 서버는 서로를 이해하기 위해 새 바이너리 인코딩 메커니즘을 사용해야 합니다. HTTP/1.x 클라이언트는 HTTP/2 전용 서버를 이해하지 못하며 그 반대의 경우도 마찬가지입니다. 다행히 클라이언트와 서버가 Google을 대신하여 필요한 모든 프레이밍 작업을 수행하므로 애플리케이션은 이러한 변경사항을 모두 인식하지 못합니다.

스트림, 메시지, 프레임

새로운 바이너리 프레이밍 메커니즘이 도입되면서 클라이언트와 서버 간에 데이터가 교환되는 방식이 변경됩니다. 이 프로세스를 설명하기 위해 HTTP/2 용어를 먼저 익혀보겠습니다.

  • 스트림: 설정된 연결 내에서 양방향 바이트 흐름으로, 하나 이상의 메시지를 전달할 수 있습니다.
  • 메시지: 논리적 요청 또는 응답 메시지에 매핑되는 전체 프레임 시퀀스입니다.
  • 프레임: HTTP/2에서 통신의 최소 단위이며, 각 통신 단위에는 최소한 프레임이 속한 스트림을 식별하는 프레임 헤더가 포함됩니다.

이러한 용어의 관계는 다음과 같이 요약할 수 있습니다.

  • 모든 통신은 단일 TCP 연결을 통해 수행되며 전달될 수 있는 양방향 스트림의 수는 제한이 없습니다.
  • 각 스트림에는 양방향 메시지를 전달하는 데 사용되는 고유 식별자와 우선순위 정보(선택사항)가 있습니다.
  • 각 메시지는 하나 이상의 프레임으로 구성된 요청 또는 응답과 같은 논리적 HTTP 메시지입니다.
  • 프레임은 특정 유형의 데이터를 전달하는 통신의 가장 작은 단위입니다(예: HTTP 헤더, 메시지 페이로드 등 다른 스트림의 프레임은 인터리브 처리된 다음 각 프레임의 헤더에 삽입된 스트림 식별자를 통해 다시 조합될 수 있습니다.

HTTP/2 스트림, 메시지, 프레임

즉, HTTP/2는 HTTP 프로토콜 통신을 바이너리 인코딩된 프레임의 교환으로 나눕니다. 그런 다음 이 프레임은 특정 스트림에 속하는 메시지에 매핑됩니다. 이러한 프레임은 모두 단일 TCP 연결 내에서 다중화됩니다. 이러한 기반은 HTTP/2 프로토콜에서 제공하는 다른 모든 기능과 성능 최적화를 가능하게 합니다.

요청 및 응답 다중화

HTTP/1.x에서 성능 향상을 위해 클라이언트가 여러 병렬 요청을 수행하려는 경우 여러 TCP 연결을 사용해야 합니다 (다중 TCP 연결 사용 참조). 이러한 동작은 HTTP/1.x 전송 모델의 직접적인 결과로 발생하며 연결당 한 번에 하나의 응답만 전송 (응답 큐)할 수 있습니다. 더 안 좋은 점은 HOL(head-of-line) 차단이 발생하고 기본 TCP 연결의 비효율적인 사용을 초래한다는 점입니다.

HTTP/2의 새로운 바이너리 프레이밍 레이어는 이러한 제한을 없애 주며, 클라이언트와 서버가 HTTP 메시지를 독립적인 프레임으로 분할하고 인터리브한 후 다른 쪽 끝에서 다시 조립할 수 있도록 하여 전체 요청 및 응답 다중화를 지원합니다.

공유 연결 내에서 HTTP/2 요청 및 응답 다중화

이 스냅샷은 동일한 연결 내에서 이동 중인 여러 스트림을 캡처합니다. 클라이언트는 DATA 프레임 (스트림 5)을 서버로 전송하고, 서버는 스트림 1 및 3의 인터리브 처리된 프레임 시퀀스를 클라이언트로 전송합니다. 따라서 진행 중인 동시 스트림이 3개 있습니다.

HTTP 메시지를 독립된 프레임으로 나누고, 인터리브한 다음, 다른 쪽에서 다시 조합할 수 있는 기능은 HTTP/2의 가장 중요한 개선사항입니다. 실제로 모든 웹 기술의 전체 스택에 수많은 성능 이점의 파급 효과가 나타나기 때문에 다음을 할 수 있습니다.

  • 여러 요청을 하나도 차단하지 않고 병렬로 인터리브 처리합니다.
  • 여러 응답을 하나도 차단하지 않고 병렬로 인터리빙할 수 있습니다.
  • 단일 연결을 사용하여 여러 요청과 응답을 동시에 전송할 수 있습니다.
  • 연결된 파일, image sprite, 도메인 샤딩과 같은 불필요한 HTTP/1.x 해결 방법을 삭제합니다 (HTTP/1.x 최적화 참조).
  • 불필요한 지연 시간을 제거하고 사용 가능한 네트워크 용량의 사용률을 개선하여 페이지 로드 시간을 줄입니다.
  • 그 밖의 다양한 기능

HTTP/2의 새로운 바이너리 프레이밍 레이어는 HTTP/1.x에서 볼 수 있는 대기 행렬 막힘 문제를 해결하며, 요청 및 응답의 병렬 처리와 전달을 지원하기 위해 여러 연결을 사용할 필요가 없습니다. 따라서 애플리케이션이 더 빠르고 단순해지고 배포 비용이 저렴해집니다.

스트림 우선순위 지정

HTTP 메시지가 다수의 개별 프레임으로 분할될 수 있고 여러 스트림의 프레임을 다중화할 수 있게 되면 프레임이 클라이언트와 서버에 의해 인터리브 처리되고 전달되는 순서가 중요한 성능 고려사항이 됩니다. 이를 용이하게 하기 위해 HTTP/2 표준에서는 각 스트림이 연관된 가중치와 종속 항목을 갖도록 허용합니다.

  • 각 스트림에는 1~256 사이의 정수 가중치가 할당될 수 있습니다.
  • 각 스트림에는 다른 스트림에 대한 명시적 종속성이 부여될 수 있습니다.

스트림 종속 항목과 가중치의 조합을 통해 클라이언트는 선호하는 응답 수신 방식을 표현하는 '우선순위 지정 트리'를 구성하고 전달할 수 있습니다. 결과적으로 서버는 이 정보를 사용하여 CPU, 메모리 및 기타 리소스의 할당을 제어하여 스트림 처리의 우선순위를 지정하고 응답 데이터를 사용할 수 있게 되면 클라이언트에 높은 우선순위 응답을 최적으로 전달할 수 있도록 대역폭 할당을 보장할 수 있습니다.

HTTP/2 스트림 종속 항목 및 가중치

HTTP/2 내의 스트림 종속 항목은 다른 스트림의 고유 식별자를 상위 요소로 참조하는 방식으로 선언됩니다. 식별자가 생략되면 스트림이 '루트 스트림'에 종속된다고 합니다. 스트림 종속 항목을 선언하면 가능한 경우 상위 스트림에 종속 항목보다 먼저 리소스를 할당해야 함을 나타냅니다. 즉, '응답 C보다 먼저 응답 D를 처리하고 전송하세요'입니다.

동일한 상위 요소를 공유하는 스트림 (즉, 동위 스트림)에는 가중치에 비례하여 리소스가 할당되어야 합니다. 예를 들어 스트림 A의 가중치가 12이고 하나의 동위 스트림 B의 가중치가 4인 경우, 이러한 각 스트림이 수신해야 하는 리소스의 비율을 결정하는 방법은 다음과 같습니다.

  1. 모든 가중치 합계: 4 + 12 = 16
  2. 각 스트림 가중치를 총 가중치로 나눕니다. A = 12/16, B = 4/16

따라서 스트림 A는 가용 리소스의 3/4을 수신하고 스트림 B는 가용 리소스의 1/4을 수신해야 하며 스트림 B는 스트림 A에 할당된 리소스의 1/3을 수신해야 합니다. 위 이미지에서 몇 가지 실습 예시를 더 살펴보겠습니다 왼쪽에서 오른쪽으로:

  1. 스트림 A와 B 중 어느 것도 상위 종속 항목을 지정하지 않으며 암시적 '루트 스트림'에 종속된다고 말합니다. A의 가중치는 12이고 B의 가중치는 4입니다. 따라서 비례 가중치에 따라 스트림 B는 스트림 A에 할당된 리소스의 1/3을 수신해야 합니다.
  2. 스트림 D는 루트 스트림에 종속되고 C는 D에 종속됩니다. 따라서 D는 C보다 먼저 전체 리소스를 할당해야 합니다. C의 종속 항목이 더 강력한 선호를 전달하기 때문에 가중치는 중요하지 않습니다.
  3. 스트림 D는 C보다 먼저 전체 리소스를 할당받아야 하고, C는 A 및 B보다 먼저 전체 리소스를 할당받아야 하며, 스트림 B는 스트림 A에 할당된 리소스의 1/3을 수신해야 합니다.
  4. 스트림 D는 E 및 C보다 먼저 전체 리소스를 할당받아야 하고, E 및 C는 A 및 B보다 먼저 동일한 할당을 수신해야 하며, 스트림 A와 B는 가중치에 비례하는 할당을 수신해야 합니다.

위의 예에서 알 수 있듯이 스트림 종속 항목과 가중치의 조합은 리소스 우선순위 지정을 위한 표현 언어를 제공합니다. 이는 종속 항목과 가중치가 다양한 리소스 유형이 많은 경우 탐색 성능을 개선하는 데 중요한 기능입니다. 게다가 HTTP/2 프로토콜을 사용하면 클라이언트가 언제든지 이러한 환경설정을 업데이트할 수 있으므로 브라우저에서 추가로 최적화할 수 있습니다. 즉, 사용자 상호작용과 기타 신호에 응답하여 종속 항목을 변경하고 가중치를 재할당할 수 있습니다.

출처당 연결 1개

새로운 바이너리 프레이밍 메커니즘이 도입됨에 따라 HTTP/2에서는 더 이상 스트림을 병렬로 다중화하기 위해 여러 개의 TCP 연결이 필요하지 않습니다. 각 스트림은 인터리브 처리되고 우선순위가 지정될 수 있는 여러 프레임으로 분할됩니다. 따라서 모든 HTTP/2 연결은 영구적이며 출처당 하나의 연결만 필요하므로 여러 성능상의 이점이 있습니다.

SPDY와 HTTP/2 모두 핵심 기능은 단일 혼잡 제어 채널에서 임의 다중화를 수행하는 것입니다. 이게 얼마나 중요하고 잘 작동하는지 감탄해요 제가 좋아하는 측정항목 중 하나는 HTTP 트랜잭션이 하나만 생성되어 트랜잭션이 모든 오버헤드를 감당할 수 있도록 만들어진 연결의 비율입니다. HTTP/1의 경우 활성 연결의 74% 가 단일 트랜잭션만 전달하며 영구 연결은 우리가 원하는 만큼 유용하지 않습니다. 하지만 HTTP/2에서는 그 수치가 25%로 떨어졌습니다. 오버헤드가 엄청나게 감소한 셈입니다. (HTTP/2는 Firefox, Patrick McManus에서 제공)

대부분의 HTTP 전송은 짧고 폭주하는 반면 TCP는 수명이 긴 일괄 데이터 전송에 최적화되어 있습니다. HTTP/2는 동일한 연결을 재사용하여 각 TCP 연결을 보다 효율적으로 사용하고 전반적인 프로토콜 오버헤드를 크게 줄일 수 있습니다. 또한 연결을 적게 사용하면 전체 연결 경로(즉, 클라이언트, 중개자, 원본 서버)에서 메모리와 처리 공간이 줄어듭니다. 이렇게 하면 전반적인 운영 비용이 절감되고 네트워크 사용률과 용량이 개선됩니다. 따라서 HTTP/2로 전환하면 네트워크 지연 시간이 줄어들 뿐만 아니라 처리량이 향상되고 운영 비용이 줄어듭니다.

흐름 제어

흐름 제어는 발신자가 원하지 않거나 처리할 수 없는 데이터로 인해 수신자를 압도하지 않도록 하는 메커니즘입니다. 수신자가 사용 중이거나 부하가 많거나 특정 스트림에 일정량의 리소스만 할당하려고 할 수 있습니다. 예를 들어 클라이언트가 우선순위가 높은 대용량 동영상 스트림을 요청했지만 사용자가 동영상을 일시중지했으며 이제 클라이언트는 불필요한 데이터 가져오기 및 버퍼링을 방지하기 위해 서버에서 동영상 전송을 일시중지하거나 제한하려고 할 수 있습니다. 또는 프록시 서버는 다운스트림이 빠르고 업스트림 연결이 느릴 수 있으며 이와 유사하게 다운스트림이 데이터를 얼마나 빠르게 전달하여 업스트림의 속도에 맞게 리소스 사용량을 제어하려고 할 수 있습니다.

위의 요구사항을 보면 TCP 흐름 제어가 생각나시나요? 문제가 사실상 동일합니다 (흐름 제어 참고). 하지만 HTTP/2 스트림이 단일 TCP 연결 내에서 다중화되기 때문에 TCP 흐름 제어가 충분히 세분화되지 않으며 개별 스트림의 전송을 조절하는 데 필요한 애플리케이션 수준 API를 제공하지 않습니다. 이를 해결하기 위해 HTTP/2는 클라이언트와 서버가 자체적인 스트림 수준 및 연결 수준 흐름 제어를 구현할 수 있게 해주는 간단한 빌딩 블록 세트를 제공합니다.

  • 흐름 제어는 방향입니다. 각 수신기는 각 스트림 및 전체 연결에 원하는 창 크기를 설정하도록 선택할 수 있습니다.
  • 흐름 제어는 크레딧 기반입니다. 각 수신기는 초기 연결 및 스트림 흐름 제어 창 (바이트 단위)을 알립니다. 이 창은 발신자가 DATA 프레임을 내보낼 때마다 줄어들고 수신기에서 보낸 WINDOW_UPDATE 프레임을 통해 증가합니다.
  • 흐름 제어는 사용 중지할 수 없습니다. HTTP/2 연결이 설정되면 클라이언트와 서버는 SETTINGS 프레임을 교환합니다. 이 프레임은 양방향으로 흐름 제어 창 크기를 설정합니다. 흐름 제어 창의 기본값은 65,535바이트로 설정되지만 수신기는 최대 창 크기(2^31-1바이트)를 크게 설정하고 데이터가 수신될 때마다 WINDOW_UPDATE 프레임을 전송하여 이를 유지할 수 있습니다.
  • 흐름 제어는 엔드 투 엔드가 아닌 홉 단위로 이루어집니다. 즉, 중개자는 이를 사용하여 리소스 사용을 제어하고 자체 기준과 휴리스틱에 따라 리소스 할당 메커니즘을 구현할 수 있습니다.

HTTP/2는 흐름 제어를 구현하기 위한 특정 알고리즘을 지정하지 않습니다. 대신 단순한 구성 요소를 제공하고 클라이언트 및 서버에 구현을 맡깁니다. 그러면 리소스 사용 및 할당을 규제하는 맞춤 전략을 구현할 뿐만 아니라 웹 애플리케이션의 실제 성능과 인지된 성능을 모두 개선하는 데 도움이 되는 새로운 전송 기능을 구현할 수 있습니다 (속도, 성능 및 인간의 인식 참고).

예를 들어 애플리케이션 레이어 흐름 제어를 사용하면 브라우저에서 특정 리소스의 일부만 가져온 후 스트림 흐름 제어 창을 0으로 줄여 가져오기를 보류한 후 나중에 다시 시작할 수 있습니다. 즉, 브라우저에서 이미지의 미리보기 또는 첫 번째 스캔을 가져와 표시할 수 있으며 우선순위가 높은 다른 가져오기가 계속 진행되도록 하고 더 중요한 리소스의 로드가 완료되면 가져오기를 재개할 수 있습니다.

서버 푸시

HTTP/2의 또 다른 강력한 새 기능은 서버가 단일 클라이언트 요청에 대해 여러 응답을 보낼 수 있는 기능입니다. 즉, 서버는 원래 요청에 대한 응답 외에도 클라이언트가 각 리소스를 명시적으로 요청할 필요 없이 추가 리소스를 클라이언트에 푸시할 수 있습니다 (그림 12-5).

서버에서 푸시 리소스의 새 스트림 (프라미스) 시작

브라우저에 이러한 메커니즘이 필요한 이유는 무엇인가요? 일반적인 웹 애플리케이션은 수십 개의 리소스로 구성되며, 모든 리소스는 서버가 제공하는 문서를 검사하여 클라이언트가 찾습니다. 따라서 추가 지연 시간을 없애고 서버가 관련 리소스를 미리 푸시하도록 할 필요가 있을까요? 서버는 이미 클라이언트에 필요한 리소스를 알고 있습니다. 바로 서버 푸시입니다.

실제로 데이터 URI (리소스 인라인 처리 참고)를 통해 CSS, JavaScript 또는 기타 애셋을 인라인한 적이 있다면 이미 서버 푸시를 직접 사용해 본 것입니다. 리소스를 문서에 수동으로 인라인하면 사실상 클라이언트가 요청할 때까지 기다리지 않고 리소스를 클라이언트에 푸시할 수 있습니다. HTTP/2로 동일한 결과를 얻을 수 있지만 추가적인 성능 이점이 있습니다 푸시 리소스는 다음과 같을 수 있습니다.

  • 클라이언트에 의해 캐시됨
  • 여러 페이지에서 재사용됨
  • 다른 리소스와 함께 다중화됨
  • 서버에서 우선순위 지정
  • 클라이언트가 거부함

PUSH_PROMISE의 기초

모든 서버 푸시 스트림은 PUSH_PROMISE 프레임을 통해 시작되며, 이 프레임은 설명된 리소스를 클라이언트에 푸시하라는 서버의 인텐트에 신호를 보내며, 푸시된 리소스를 요청하는 응답 데이터보다 먼저 전송되어야 합니다. 이 전달 순서는 매우 중요합니다. 이러한 리소스에 대한 중복 요청이 생성되지 않도록 하기 위해 클라이언트는 서버가 푸시할 리소스를 알아야 합니다. 이러한 요구사항을 충족하는 가장 간단한 전략은 약속된 리소스의 HTTP 헤더만 포함된 모든 PUSH_PROMISE 프레임을 상위 요소의 응답 (DATA 프레임)보다 먼저 전송하는 것입니다.

클라이언트가 PUSH_PROMISE 프레임을 수신하면 원하는 경우 RST_STREAM 프레임을 통해 스트림을 거부할 수 있습니다. 예를 들어 리소스가 이미 캐시에 있기 때문에 이러한 상황이 발생할 수 있습니다. 이는 HTTP/1.x에 비해 크게 개선된 기능입니다. 반면, HTTP/1.x에서 널리 사용되는 '최적화'인 리소스 인라인 처리의 사용은 '강제 푸시'와 동일합니다. 클라이언트는 인라인 처리된 리소스를 개별적으로 선택 해제, 취소하거나 처리할 수 없습니다.

HTTP/2를 사용하면 클라이언트가 서버 푸시의 사용 방식을 완전히 제어할 수 있습니다. 클라이언트는 동시에 푸시되는 스트림 수를 제한하거나, 초기 흐름 제어 창을 조정하여 스트림이 처음 열릴 때 푸시되는 데이터의 양을 제어하거나, 서버 푸시를 완전히 사용 중지할 수 있습니다. 이러한 환경설정은 HTTP/2 연결이 시작될 때 SETTINGS 프레임을 통해 전달되며 언제든지 업데이트될 수 있습니다.

푸시된 각 리소스는 인라인 처리된 리소스와 달리 클라이언트에서 개별적으로 다중화하고, 우선순위를 지정하고, 처리할 수 있는 스트림입니다. 브라우저에서 시행하는 유일한 보안 제한사항은 푸시된 리소스가 동일 출처 정책을 준수해야 한다는 것입니다. 즉, 서버는 제공된 콘텐츠에 대한 권한이 있어야 합니다.

헤더 압축

각 HTTP 전송에는 전송되는 리소스와 그 속성을 설명하는 헤더 집합이 있습니다. HTTP/1.x에서 이 메타데이터는 항상 일반 텍스트로 전송되며 전송당 500~800바이트의 오버헤드가 추가되며, HTTP 쿠키를 사용할 경우 KB가 더 늘어날 때도 있습니다. (프로토콜 오버헤드 측정 및 제어 참조) 이러한 오버헤드를 줄이고 성능을 개선하기 위해 HTTP/2는 간단하지만 강력한 두 가지 기술을 사용하는 HPACK 압축 형식을 사용하여 요청 및 응답 헤더 메타데이터를 압축합니다.

  1. 이를 사용하면 전송된 헤더 필드가 정적 Huffman 코드를 통해 인코딩되어 개별 전송 크기가 줄어듭니다.
  2. 클라이언트와 서버 모두 이전에 표시된 헤더 필드의 색인이 생성된 목록을 유지하고 업데이트해야 합니다 (즉, 공유 압축 컨텍스트를 설정함). 그런 다음 이를 참조로 사용하여 이전에 전송된 값을 효율적으로 인코딩해야 합니다.

Huffman 코딩을 사용하면 전송 시 개별 값을 압축할 수 있으며 이전에 전송된 값의 색인이 생성된 목록을 사용하면 전체 헤더 키와 값을 효율적으로 조회하고 재구성하는 데 사용할 수 있는 색인 값을 전송하여 중복 값을 인코딩할 수 있습니다.

HPACK: HTTP/2용 헤더 압축

한 가지 추가 최적화로, HPACK 압축 컨텍스트는 정적 및 동적 테이블로 구성됩니다.정적 테이블은 사양에 정의되어 있으며 모든 연결에서 사용할 가능성이 높은 공통 HTTP 헤더 필드 (예: 유효한 헤더 이름) 목록을 제공합니다. 동적 테이블은 처음에는 비어 있으며 특정 연결 내에서 교환된 값에 따라 업데이트됩니다. 따라서 각 요청의 크기는 이전에 보지 않은 값에 정적 Huffman 코딩을 사용하고 각 면의 정적 또는 동적 테이블에 이미 존재하는 값에 대해 색인을 대체하여 축소됩니다.

HPACK의 보안 및 성능

초기 버전의 HTTP/2 및 SPDY에서는 커스텀 사전과 함께 zlib를 사용하여 모든 HTTP 헤더를 압축했습니다. 이를 통해 전송되는 헤더 데이터 크기가 85% ~88% 감소하고 페이지 로드 시간 지연 시간이 크게 개선되었습니다.

업로드 링크가 375Kbps에 불과한 저대역폭 DSL 링크의 경우 특히 요청 헤더 압축을 통해 특정 사이트 (즉, 리소스 요청을 많이 발행한 사이트)에서 페이지 로드 시간이 크게 개선되었습니다. 헤더 압축을 통해 페이지 로드 시간이 45~1,142ms 단축된 것으로 나타났습니다. (SPDY 백서, chromium.org)

그러나 2012년 여름, TLS 및 SPDY 압축 알고리즘에 대한 'CRIME' 보안 공격이 게시되어 세션 하이재킹이 발생할 수 있었습니다. 그 결과, zlib 압축 알고리즘이 HPACK으로 대체되었습니다. HPACK은 발견된 보안 문제를 해결하고, 효율적이고 간단하며, 올바르게 구현하고, HTTP 헤더 메타데이터를 적절하게 압축할 수 있도록 특별히 설계되었습니다.

HPACK 압축 알고리즘에 대한 자세한 내용은 IETF HPACK - HTTP/2용 헤더 압축을 참조하세요.

추가 자료