Learning HTTP/2
HTTP/2에 대해 배우고, HTTP 발전 과정과 웹 최적화 기법들을 자세히 살펴보는 글입니다.
Contents

읽게된 계기
마음먹고 서점에서 소설책을 하나 사려고 했었는데, 사려고 했던 책이 마침 품절 상태였다. 빈손으로 서점에서 오기 아쉬워서 책구경을 하던 중에, 이 책을 발견하게 되었다. 대학생 시절, 네트워크 시간에 HTTP2 에 대해 잠깐 훑어보기만 했었다. UDP 기반의 HTTP3 도 웹 표준으로 등록된 상황에서, HTTP2 스펙에 대해서도 공부해봐야겠다는 생각이 들어서 구매해서 읽게 되었다.
HTTP의 발전과정
Evolution of HTTP 페이지에서는 HTTP 의 발전 과정에 대해 상세히 설명되어 있다. 
HTTP/0.9
엄밀히 말하자면, HTTP/0.9 는 존재하지 않는 프로토콜이다. 왜냐하면 HTTP 프로토콜이 처음 세상에 나왔을 때에는, 버전 번호가 붙어있지 않았기 때문이다. HTTP/1.0 이 나온 이후, 이전 버전과 구분하기 위해 HTTP/0.9 라는 이름으로 불리게 되었다. HTTP/0.9 의 스펙은 한 페이지의 문서 로 정리될 만큼 스펙이 간단했다. HTTP/0.9 에서는 단 하나의 메서드(GET) 만 사용할 수 있었고, 어떠한 헤더도 없었으며, 텍스트만 다룰 수 있는 HTML 만 처리할 수 있도록 설계되었다. 1990년대 초반까지만 해도, 인터넷이 문서 열람의 성격을 지니고 있었다고도 볼 수 있겠다.  또 하나 눈여겨 볼 점은, HTTP 요청은 멱등(idempotent)하다 라는 구절이 있는데, 이러한 점이 HTTP/1.0 에 크게 영향을 미친 것 같다. 각 요청들은 상태값을 지니지 않는다는 사실은 안전한 통신을 하는 데에는 도움이 되었지만, 후대에는(HTTP2) 성능 최적화를 위해 점점 상태값을 지니게 바꿔나갔다.
HTTP/1.0
HTTP/0.9 가 발표된 이후, HTTP의 사용이 계속 증가하였다. 1995년에는 80번 포트에서 HTTP 트래픽을 처리하는 서버가 18,000대를 넘어섰다. 이에 따라, 1996년에는 HTTP/1.0 이 RFC 1945 로 발표되었다. HTTP/0.9 가 한 페이지 분량으로 정리될 수 있었던 데에 반해, HTTP/1.0 은 60페이지 정도에 달했다. HTTP/1.0 에는 다음의 개념들이 도입되었다.
- Header
 - Response Code
 - Redirects
 - Errors
 - Conditional Requests
 - Encoding, Compression
 - Request Methods (GET 을 포함한 다른 메소드들)
 
HTTP/1.0 은 HTTP/0.9 의 부족한 기능들을 많이 채워 주었지만, 다음과 같은 문제들은 여전히 남아있었다.
- 여러 요청 사이에 연결을 유지하는 기능 (이후의 
Connection헤더) - Host Header 의 부재
 - Caching 옵션이 빈약
 
HTTP/1.1
1999년에, RFC 2616 이 발표되었다. 이 프로토콜은 지금까지 20년 넘게 사용되고 있다. 1.1 버전에서는 위에 적었던 문제점들이 많이 해결되었다.
Host헤더가 추가되어서, 가상 호스팅(하나의 IP주소로 다수의 웹 프로퍼티를 제공) 을 할 수 있게 되었다.- 새 연결 지시자를 사용하면, 웹 서버는 응답 후에 연결을 끊을 필요가 없어졌다. (HTTP Persistent Connection)
 OPTIONSMethodUpgradeHeader (Websocket 으로 변환 시에도 사용됨)RangeRequestTransfer-EncodingCompressionPipelining
HTTP/1.1는 지금까지도 많이 사용되고 있는 프로토콜이지만, 분명히 남아있는 문제들이 있다.
HOL 블로킹 현대의 일반적인 웹페이지의 경우, 특정 호스트에서 단 하나의 개체만 fetch 해오지 않는다. 특정 도메인에 모든 이미지를 넣어둔 웹페이지의 경우, HTTP/1 은 그 이미지들을 동시에 요청할 수 있는 메커니즘을 제공하지 않는다. 이를 해결하기 위해 HTTP/1.1 에서
Pipelining기능을 추가하였지만, 브라우저에서는 여전히 전송된 순서대로 하나씩 응답을 수신한다. 현대화된 브라우저는 특정 호스트에 최대 6개의 연결을 열고 각 연결로 요층을 전송해 어느 정도 병렬 처리가 가능하다. 하지만 각 연결은 여전히 HOL블로킹의 영향을 받을 수 있다.TCP의 비효율적 사용 (혼잡 제어)

TCP 는 데이터를 가장 빨리 전송하는 수단이라기보다는,
데이터를 가장 신뢰성 있게 보낼 수 있는 방법이다. TCP 의 패킷 전송 사이즈는 느린 시작(Slow Start) 를 통해 결정되는데, 첫 패킷 크기는 10으로 결정되고, 수신 측에서 문제가 없다면, 하나씩 패킷 수를 증가시킨다. 만약 수신 측에서 패킷 손실이 일어난다면, 패킷의 크기를 현재 크기의 절반으로 낮춘다. 이러한 방식을 AIMD(Additive Increase/Multiplicative Decrease)라 부른다. 이러한 전략은 안전한 통신을 보장하지만, TCP 연결 초기에는 실제로 가용할 수 있는 패킷의 양에 훨씬 미달하게 통신을 하게 된다. 재미있는 사실은, 이러한 이유로 인해, 프론트엔드 소스코드의 크기는 14.6KB 이하일 때 가장 쾌적하게 동작한다.비대한 메시지 헤더 HTTP/1 프로토콜에서는, 헤더 크기를 압축할 수 있는 방법이 없다. HTTP아카이브에 따르면, 2016년 기준으로 요청 헤더의 평균 크기는 460byte 이다. 한 패킷의 전체 크기가 1500byte 라는 점을 생각해 볼 때, 패킷의 상당히 많은 부분이 헤더에 할당되고 있다는 점을 알 수 있다.
제한적인 우선순위 위에서 말했듯이, HTTP 요청 과정에서
HOL블로킹이 발생할 수 있다. 우리가 웹 브라우저를 사용하는 일반적인 상황을 상상해 보면, HOL블로킹은 꽤 짜증나는 상황을 불러올 수 있다. 구글에 검색을 했을 때, 특정 이미지가 네트워킹 과정에서 다운로드 되지 않아, 브라우저의 화면 자체가 렌더링되지 않는다면 매우 짜증날 것이다.
HTTP/1.1 가 발표된 이후, 한동안 새로운 프로토콜이 발표되지 않았다. 하지만 웹은 빠른 속도로 발전하고 있었고, 새로운 프로토콜에 대한 구상이 이루어지고 있었다.
SPDY
2009년, 구글의 마이크 벨시와 로베르토 페온은 HTTP의 대안으로 SPDY를 제시했다. HTTP를 대체하려는 제안은 이전에도 있었지만, 모두 HTTP/1.1 과의 호환성을 지켜야 한다는 점 때문에, 눈에 띄는 변화가 있지는 않았다. SPDY 는 HTTP/1.1 과의 호환성을 포기하고, 다양한 변화를 시도하였다. 다중화(multiplexing), 프레이밍(framing), 헤더 압축(header compression) 과 같은 새로운 시도가 있었다. 주요 브라우저들 대부분이 SPDY 프로토콜을 지원하기 시작했고, 많은 서버와 프락시도 이에 맞추어 SPDY 를 지원해야만 했다.
HTTP2
2012년 초, SPDY 의 개선점들을 참고하여 HTTP2 에 대한 워킹 그룹이 구성되었다. HTTP/2.0은 다음을 목표로 한다.
- 대부분의 경우에서 최종 사용자의 Latency 를 TCP를 사용하는 HTTP/1.1보다 실질적으로 현저히 개선한다.
 - HTTP의 HOL 블로킹 문제를 해결한다.
 - 병럴 처리를 위해 서버에 다중 연결할 필요가 없다. TCP의 사용, 특히 혼잡 제어와 관련된 동작을 계선한다.
 - HTTP/1.1 의 의미 체계를 유지한다. HTTP 메서드, 상태 코드, URI, 헤더 필드 외에도 기존에 문서화된 것을 최대한 활용한다.
 - HTTP/2.0 이 HTTP/1.x 와 호환성을 유지하는 방법, 특히 중재자(intermediary)를 며오학히 정의한다.
 - 새로운 확장 기능과 정책을 적절히 사용할 수 있도록 명확하게 한다.
 
HTTP/1 와 대조되는 HTTP/2 만의 특징들을 간단하게 정리해보자면 다음과 같다.
바이너리 프로토콜 HTTP/2 는 바이너리 프레임 형식의 프로토콜이다. 이 때문에 기계는 쉽계 파싱할 수 있지만, 사람이 읽기는 어렵다
헤더 압축 헤더를 압축하여, 바이트의 전송을 줄이는 데 효과가 있다.
다중화 요청과 응답이 뒤섞여, 여러 개의 요청이 병렬적으로 처리된다.
암호화 전송 중인 데이터의 대부분이 암호화된다.
연결 HTTP/2 가 지원되지 않는 클라이언트들도 많이 존재하기 때문에,
특정한 octet 스트림을 연결의 첫 번째 데이터로 전송한다. 이 스트림은 다음과 같다.\PRI * HTTP/2.0\r \r SM\r \r만약 HTTP/2 가 지원되지 않는 경우, 명시적인 오류가 발생한다. 이후에는SETTINGS프레임을 주고받는다.프레임 HTTP/1 의 간단한 예시를 살펴보자
GET / HTTP/1.1
Host: www.google.com:443
Proxy-Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
HTTP/1 은 텍스트 구분 기반의 프로토콜이다. crlf(\r
) 을 기준으로 데이터를 파싱한다. 텍스트 구분 기반의 프로토콜은 다음과 같은 문제점들이 있다.
- 한 번에 하나의 요청,응답만 전송이 가능하다. 모든 전송이 끝날 때까지 파싱해야 한다
 - 파싱에 얼마나 많은 메모리가 사용될지 알 수 없다.
 
반면에, 프레임을 사용하면 수신자는 무엇을 수신할 지 미리 알 수 있다. 예를 들어, 첫 3바이트를 읽으면 데이터의 크기를 미리 알아낼 수 있다. 또한 HTTP/2 의 프레임 유형은 4번째 바이트를 파싱하면 된다. (아래 그림 참조)

HTTP/2 프레임 헤더의 구조
프레임의 유형은 다음과 같다.
| 이름 | ID | 설명 |
| --- | --- | --- |
| DATA | 0x0 | 특정 스트림의 핵심 내용을 전송한다. |
| HEADERS | 0x1 | HTTP 헤더를 포함하며, 선택적으로 우선순위를 포함할 수 있다. |
| PRIORITY | 0x2 | 스트림 우선순위와 의존성을 표시 또는 변경한다. |
| RSTSTREAM | 0x3 | 엔드포인트가 스트림을 닫도록 허용한다(보통, 오류가 발생한 경우) |
| SETTINGS | 0x4 | 연결 초기에 매개변수를 주고받는다. |
| PUSHPROMISE | 0x5 | 서버가 무언가를 보내려 핟나느 사실을 클라이언트에 알려준다. |
| PING | 0x6 | 연결을 테스트하고 RTT를 측정한다. |
| GOAWAY | 0x7 | 상대방이 새로운 스트림을 수신했음을 엔드포인트에 알려준다. |
| WINDOW_UPDATE | 0x8 | 엔드포인트가 얼마나 많은 바이트를 수신할 수 있는지 주고받는다. (흐름 제어에 사용) |
| CONTINUATION | 0x9 | HEADER 블록을 확장하는데 사용한다. |
스트림 스트림은 하나의 연결 위에서 개별 HTTP 요청/응답의 쌍을 구성하는 일련의 프레임 모음이다. 클라이언트는 요청을 할 때 새 스트림을 개시한다. 그러면 서버는 동일한 스트림 위에서 응답한다. 덕분에 다수의 요청과 응답이 서로를 차단하지 않고 뒤섞여 배치될 수 있다.
흐름 제어 HTTP/1 에서는 서버가 클라이언트의 수신 가능 속도를 고려하여 데이터를 전송하는 반면(Slow Start), HTTP/2 에서는 클라이언트가 전송 속도를 조절하는 기능을 제공한다. 흐름 제어에 관련된 정보는
WINDOW_UPDATE(윈도우 크기를 업데이트) 프레임을 전송하여 자신이 수신할 수 있는 바이트 수를 갱신한다. HTTP/1 에서는 혼잡 상황에 빠지고 난 후에(패킷 손실이 일어난 후) 윈도우 크기가 조정되었지만, HTTP/2 에서는 클라이언트가 선제적으로 윈도우 크기를 조절하므로, 사전에 혼잡 상황을 막을 수 있다. 다음은 HTTP/2 상황에서의 흐름 제어 예시이다.
모든 스트림의 시작 윈도우 크기는 기본적으로 65,536(2^16-1)bytes이다. 클라이언트 엔드포인트 A가 이 기본값을 유지하고 상대측 B가 10,000bytes를 보낸다고 가정해보자. B는 윈도우 크기를 계속 추적한다(현재 55,535bytes). 이제, A가 5,000bytes를 처리한 후, 자신의 윈도우 크기가 이제 60,535bytes라고 알려주는 WINDOW_UPDATE 프레임을 보낸다. B는 이 프레임을 수신한 후, 큰 파일(4GB)을 보내기 시작한다. B는 현재 윈도우 크기(이 경우 60,535 bytes) 까지만 보낼 수 있으며, 그 후 A가 더 많은 바이트를 수신할 준비가 되었음을 알려주기를 기다려야 한다. 이런 방식으로, A는 B가 데이터를 보낼 수 있는 최대량을 제어할 수 있다.
- 우선순위
브라우저는 HTML을 수신하고 나면, 화면을 그리기 위해 CSS/Javascript 등을 요구한다. 다중화가 없다면, 응답이 완료되기를 기다린 후에 새 개체를 요청할 수 있다. HTTP/2에서는 클라이언트는 모든 자원 요청을 동시에 보낼 수 있고, 서버는 그 요청들의 처리를 한번에 시작할 수 있다. 여기에서, 만약 우선순위가 없다면 서버는 모든 것을 거의 동시에 전송하여 덜 중요한 요소가 더 중요한 요소를 방해하기 될 것이다.
 
HTTP/2 는 스트림 의존성을 통해 이를 해결한다. HEADERS, PRIORITY프레임을 사용하면, 클라이언트는 필요한 콘텐츠의 순서를 쉽게 주고받을 수 있다. 이는 의존성 트리와 그 트리 안에 상대적인 가중치를 선언하여 이루어진다.
서버 푸시 특정 개체의 성능을 개선하는 가장 좋은 방법은 그 개체가 요청되기 전에 브라우저 캐시에 미리 가져다 두는 것이다. 이것이 바로 HTTP/2의 Server Push 기능의 목표다. 서버는 가까운 미래에 특정 개체가 필요하리라는 것을 알 수 있기 때문에 푸시를 통해 클라이언트에 개체를 미리 전달할 수 있다.
헤더 압축(HPACK) 기본적으로 HTTP/1 는 멱등적이라는 성질로 인해, 여러 요청 사이에 완전히 새롭고 고유한 바이트는 거의 없다. 이는 신뢰성있는 통신을 가능하게 해 주지만, 효율적이지 않은 통신을 하게 만들기도 한다. 이것이 압축이 필요한 이유이다. 다음의 간단한 예시를 살펴보자.
요청 #1
    :authority: www.foo.com
    :method: GET
    :path: /
    :scheme: https
    accept: text/html,application/xhtml+xml
    aaccept-language: en-US,en:q=0.8
    cookie: foo=bar
요청 #2
    :authority: www.foo.com
    :method: GET
    :path: /style.css
    :scheme: https
    accept: text/html,application/xhtml+xml
    aaccept-language: en-US,en:q=0.8
    cookie: foo=bar1
두 번째 요청 중 많은 부분이 첫 번째 요청을 반복하고 있다. HPACK 방식을 사용하면, 첫 번째 요청에서 바뀐 부분만 요청으로 보낼 수 있다. RFC 7541 을 살펴보면, HPACK 에 대해 상세히 기술되어 있다.
Web Optimization
오늘날 사용되는 웹 최적화 기법은 HTTP/1 에서만 유효한 기법들도 있고, 심지어 어떤 부분들에 대해서는 HTTP/2 에서는 안티패턴으로 여겨지는 기법들도 있다. 한 번씩 읽어보았을 때에는, 웹 최적화를 위해 각종 꼼수들을 도입한 느낌이었는데, 특히 FE 는 프로젝트들에 적용시켜도 좋은 부분들도 있었다.
DNS 조회 최적화 DNS 조회는 호스트와 연결이 수립되기 전에 이루어져야 하므로, 이 조회 절차는 가능한 한 빨라야 한다. *고유한 도메인/호스트이름의 수를 제한 *핸드쉐이크 후 Connection 수립을 위해 필요한 시간을 절약할 수 있다. *초기 HTML이나 응답에 대해 DNS prefetch 를 활용 *초기 HTML 을 내려받아 처리하는 동안 페이지에 있는 Host 이름들의 DNS 조회를 시작한다.
<link rel="dns-prefetch" href="//ajax.googleapis.com">TCP 연결 최적화 새 Connection 을 여는 일은 시간이 오래 걸리는 절차일 수 있다.
preconnect를 활용한다면, 필요하기 전에 미리 연결을 수립함으로써 폭포수 임계 경로 연결시간을 제거해준다.<link rel="preconnect" href="//fonts.example.com" crossorigin>리다이렉션을 지양
클라이언트에 캐싱 로컬에서 콘텐츠를 검색하면, 가장 속도가 빠르고 ISP나 CDN 공급자로부터 요금을 청구받을 일도 없다. 브라우저에 TTL 을 설정하여, 브라우저에서 자원을 캐싱할 수 있다. 클라이언트 캐싱 TTL 을
Cache-ControlHTTP 헤더의max-age(초) 키를 사용하거나Expires헤더를 사용해 설정할 수 있다.네트워크 경계에 캐싱 네트워크 경계(Edge) 에 캐싱하면 모든 사용자가 클라우드 내의 공유 캐시의 혜택을 얻을 수 있으므로, 더 빠른 사용자 경험을 제공하고 인프라에서 많은 양의 트래픽을 덜어낼 수 있다. 캐싱할 수 있는 자원은 다음과 같다. *여러 사용자 간 공유 가능한 자원 *어느 정도 노후도를 수용할 수 있는 자원
조건부 캐싱 클라이언트가 서버에
그 개체가 변경되었으면 보내달라, 그렇지 않으면 그냥 동일하다고 알려달라라고 요청하는 기능이다. 조건부 캐싱을 사용하는 방법은 다음과 같다. *If-Modified-SinceHTTP 헤더를 요청에 포함한다. 서버는 최신 콘텐츠가 헤더에 명시된 시점 이후에 갱신된 경우에만 전체 콘텐츠를 반환하며, 그렇지 않으면 응답 헤더에 새 Timestamp Date 를 포함한 304 응답을 반환한다. *개체를 고유하게 식별하는 엔티티 태그, 즉 ETag를 요청에 포함한다. 서버는 ETag를 헤더에 포함하여 개체 자체와 함께 재공한다. 서버는 현재 ETag를 요청 헤더의 ETag와 비교한 후, 동일하면 304를 반환하고, 다르면 전체 콘텐츠를 반환한다.
예시로, S3 를 통해 캐싱이 되어있는 페이지의 경우, 브라우저에서 다음과 같은 요청을 보내게 된다.
#Request
GET /post/dev/ HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control: max-age=0
Connection: keep-alive
Host: kim.heejae.info
If-Modified-Since: Fri, 01 Apr 2022 11:07:03 GMT
If-None-Match: W/"77588-17fe4cea828"
Referer: https://kim.heejae.info/post/dev/210
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
sec-ch-ua: "Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
#Response
HTTP/1.1 304 Not Modified
Server: nginx/1.18.0
Date: Sun, 11 Sep 2022 03:25:55 GMT
Connection: keep-alive
X-Powered-By: Express
Vary: Origin
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Fri, 01 Apr 2022 11:07:03 GMT
ETag: W/"77588-17fe4cea828"
실제 브라우저에서는(크롬 기준), Etag 헤더가 아닌 If-None-Match Header  헤더도 조건부 캐싱에 사용되는듯 하다. 재미있는 점은, 크롬 강제 새로고침(CMD+SHIFT+R) 을 하면 ETag 값을 변경시켜서 요청을 날릴 줄 알았는데, 실제로 해보니 브라우저가 Header 에 If-None-Match Header 를 빼고 요청을 보내고 있었다.
- 압축과 축소화
HTML 태그에서 인간의 가독성을 위해 도움이 되는 Code Formatting 을 없애고, 용량을 줄인 뒤 요청을 전송한다. 또한 
gzip,deflate등을 통해 요청을 더더욱 압축할 수 있다. 
기타
- curl 에서 http2 사용하기
간단하지만, curl 에서 
--http2옵션을 주면 HTTP/2 통신을 할 수 있다. 
~  $ curl -v --http2 https://www.wiselycompany.com
*   Trying 13.225.112.128:443...
* Connected to www.wiselycompany.com (13.225.112.128) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=*.wiselyshave.com
*  start date: Aug  9 00:00:00 2022 GMT
*  expire date: Sep  7 23:59:59 2023 GMT
*  subjectAltName: host "www.wiselycompany.com" matched cert's "*.wiselycompany.com"
*  issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon
*  SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x126013a00)
> GET / HTTP/2
> Host: www.wiselycompany.com
> user-agent: curl/7.79.1
> accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
< content-type: text/html
< content-length: 5673
< date: Sun, 11 Sep 2022 03:20:25 GMT
< cache-control: no-cache
< last-modified: Thu, 08 Sep 2022 09:00:46 GMT
< etag: "32a04edf34ba6617cba67adf63f50115"
< server: AmazonS3
< vary: Accept-Encoding
< x-cache: Miss from cloudfront
< via: 1.1 d3e79189173c2fa1e1bfddff07afdfe4.cloudfront.net (CloudFront)
< x-amz-cf-pop: ICN54-C1
< x-amz-cf-id: vMG1YOKuj6mMQtrkyjjetqtXA4-Bqm5EZkqZVetOOWaL6PpRiieiWw==
<
<!DOCTYPE html><html lang="ko-KR"><head>
...
- Proxyman 에서의 HTTP/2 책을 읽고 Proxyman 에서 HTTP/2 패킷 분석을 해보려 했지만, Proxyman 에서 Proxy 를 사용하면, HTTP/2 사용이 가능한 요청도 HTTP/1 요청으로 다운그레이드된다. Proxyman Repository 에 해결되지 않은 Issue 를 살펴보면, 기능이 지원되길 좀 더 기다려봐야 할 것 같다.
 
이것도 읽어보세요