Http/1.1를 조사했다.
개요
RFC2616를 읽고 HTTP/1.1에 대해 알아보자.
동기
우리는 컴퓨터를 키고 당연히 웹브라우저를 킨다.
컴퓨터를 킨다는 소리는 어쩌면 웹브라우저를 킨다는 소리와 사실상 동의어가 되어버렸다.
그리고 웹브라우저에 돌아갈 서비스를 만들기 위해 개발자는
React, Vue, Svelte, Angular, Preact, Remix, SolidJS, Next.js, Nuxt.js, Blitz.js, Aleph.js, vno...
Node.js, Deno, Express, Koa, Nest.js, fastify, Socket.io, Django, flask, Sinatra, Ruby on rails, Laravel...
PostgreSQL, MariaDB, MySQL, MongoDB, DynamoDB, Graphql, Redis...
Docker, Kubernetes, Vargant, Apache, nginx, AWS...
헤아릴 수 없는 환경, 라이브러리, 프레임워크, 언어가 있지만 우리 모두 한 줄의 메세지를 보기 위해 프로그래밍 한다.
HTTP/1.1 200 OK
HTTP/1.1 정의한 RFC2616를 알자
- RFC2616: Hypertext Transfer Protocol HTTP/1.1
- 1999년 6월 IETF 공개
- 쓸데는 없지만 알아두면 좋은 RFC2616를 정의한 유명인
RFC....
RFC2616에 대해 조사하다보니 이 RFC는 폐기되었고, 2014년에 RFC7230, RFC7231, RFC7232, RFC7233, RFC7234, RFC7235 로 정의한 HTTP1.1
를 사용한다는 사실을 알게되었다.
우선 각 문서가 무슨 역할을 하는가 정리해보면,
- RFC7230: HTTP1.1 메시지 문법 및 라우팅
- RFC7231: HTTP의 의미론과 내용
- RFC7232: HTTP 조건 REQUEST
- RFC7233: HTTP 범위 REQUEST
- RFC7234: HTTP 캐시처리
- RFC7235: HTTP 인증
그 외
무엇이 변했을까?
- 보안상 문제 수정
- RFC에 없던 사실상 표준을 정의
RFC(7230-7235) 부록에 RFC2616 변경점 표기됨
주요 변경점
RFC7230: HTTP1.1 메시지 문법 및 라우팅
- HTTP/1.1
대문자
로 한정, 버전 표기도한 자리
로 한정 (eg 1.1
가능, 1.12
안됨) - 헤더의
악용 가능성 있는 whitespace 및 복수행 헤더 폐기
- body length
명시
- chunk length에
chunk head, trailer 포함 안 시킴
- Connection: close 송신 동작
명시
RFC7231: HTTP의 의미론과 내용
- charset(ISO-8859-1)
폐기
- Same Origin에서는
Referer 보낼 것
- about:blank 추가, Referer 누락하는 것이 아니라
명시적
으로 존재하지 않음을 선언 - 일관성을 위해 GET Request가 body를 갖도록
허용
- 204, 404, 405, 414, 501
캐시 허용
- Content-MD5 헤더
삭제
Referer: 사람들이 어디로부터 와서 방문 중인지 인식할 수 있도록 해주는 것
RFC7232: HTTP 조건 Request
- If-Modified-Since 등 조건 Request 정의
RFC7233: HTTP Range Request
- Range Request
정의
RFC7234: HTTP 캐시처리
- Caching Cache 정의
RFC7235: HTTP 인증
- Basic/Digetst 인증
그 외
RFC7236: HTTP 인증 스킴 등록
- 인증 종류
정의
RFC7237: HTTP 메서드 등록
- HTTP 메서드 추가 정의 예약
- 주로 WebDAV 메서드에 관한 것
RFC7238: HTTP 308 상태 코드 (리다이렉트에 관한)
- 새로운 리다이렉트 상태
영속적 | 일시적 | |
---|---|---|
POST => GET 메서드 변경 허용 | 301 | 302 |
POST => GET 변경 허용 안함 | 308 | 307 |
RFC7239: HTTP 확장
- Forwarded 헤더 표준화
- X-Fowared-For, X-Forwared-Proto 등 헤더는 X가 의미하듯 RFC 표준 헤더가 아니지만 사실상 표준임
:::note 부록: Forwarded 헤더
- "by": request 받은 프록시 Customer Facing 식별자
- "for": request처 (XFF와 같음)
- "host": 프록시를 받은 Host 헤더
- "proto": 프록시를 받은 프로토콜
Forwarded: for="_gazonk"
Forwarded: for=192.0.0.1;proto=http;by=203.0.0.1
Forwarded: for=192.0.0.1, for=198.0.0.1
:::
HTTP를 알자
사실상 여기가 본론...
HTTP 이름을 알아보자
HTTP는 이름에 이것이 무엇인가 가장 간략하게 설명되어 있다.
Hyper Text Transfer Protocol
여기서 Hyper Text
는 독자가 문서에서 다른 문서로 접근할 수 있는 텍스트를 의미하며 Hyper Media
또한 같은 맥락이다. 이러한 다른 것으로의 접근을 기반으로 거미줄처럼 얽혀있어 우리는 Web이라 한다.
나머지 Transfer Protocol
은 말그대로 전송 규약으로 무엇인가 전송하기 위한 약속을 의미한다. HTTP 위치를 그림으로 표현하면 이하와 같다.
Protocol 만 따로 떼어내 언급하면, 컴퓨터 내부에서, 또는 컴퓨터 사이에서 데이터의 교환 방식을 정의하는 규칙 체계. 기기 간 통신은 교환되는 데이터의 형식에 대해 상호 합의가 필요하며, 이런 형식을 정의하는 규칙의 집합을 프로토콜이라고 한다.
여담으로 우리는 흔히 HTTP 프로토콜이라하지만 HTTP는 그 자체에 프로토콜이라는 말이 들어가므로
HTTP 프로토콜
이라함은역전앞
같이 동의어를 두 번 말하는 격이다...
수 많은 프로토콜을 보고 싶다면 여기
규약
버전표기
HTTP
+ /
+ 숫자
+ .
+ 숫자
예시 HTTP/1.1
호환
프록시/게이트웨이는 실제 자신의 버전보다 높은 표시를 사용해서 메세지를 발송하면 절대
안 됨
상위 버전 요구가 수신되었으면 프록시 / 게이트웨이는
- 요구 버전을 내리거나
- 에러를 발송하거나
- 터널로 전환해야 한다
프록시 / 게이트웨이 버전보다 낮은 요구는 상위 버전으로 올릴 수 있으나 요구 받은 버전의 주요 버전은 반드시 동일해야 한다.
URI
URI는 www부터 URL, URN 등등 식별해주는 정형화된 문자열
스킴://호스트[:포트] [절대경로]
http://호스트[:포트] [절대경로]
- 비어 있거나 명시되지 않은 포트는 기본 80
- 호스트는 대소문자 구별 X
- 스킴은 대소문자 구별 X
- 비어 있는 절대경로는 "/"와 동일
http://abc.com
=== http://ABC.com
=== http://ABC.COM/
역사를 알아보자
HTTP/0.9
- 팀 버너스리가 1990년에 발명한 프로토콜을 HTTP/0.9라 함
- 헤더가 없음
- 메서드는 GET만 존재
HTTP/1.0
- IETF에서 표준화된 최초의 버전
- 버전 명시
- 헤더 도입
- GET 메서드 외의 메서드 도입(확장성)
HTTP/1.1
- HTTP의 완성
- Connection 재사용 가능
- 파이프라이닝
- chunk response
- Accept 헤더를 통한 content 협상으로 클라이언트-서버 교환에서 적합한 컨텐츠 제안가능
- 복잡한 캐시 컨트롤
- 지속적 접속
- Host 헤더
- URL === URI === URN 으로 봐도 무방
- MIME 차용
- Web의 거의 대부분이
이 프로토콜
-
- 텍스트 프로토콜 => 이진 프로토콜
- 다중화 프로토콜, 순서 제거
- 중복, 오버헤드 제거, 헤더 압축
- 서버에서 클라이언트 캐시를 서버 푸시 메커니즘으로 데이터를 넣도록 허용
-
- 특이사항:
UDP
이용
- 특이사항:
:::note
부록: Request - Response 체인
단순한 상황
request chain -------- response chain
UA------------V-----------O
- UA: user agent
- V: connect
- O: origin server
복잡한 상황
request chain -------- response chain
UA----V----A----V----B----V----C----V----O
- UA: user agent
- V: connect
- O: origin server
- ABC: 중개자
복잡한 상황2
터널이 아니라면 내부 캐시 사용할 수 있다
캐시된 응답이 있으면 체인은 간결해짐
O가 B 사본을 가지고 있을 때이다.
request chain -------- response chain
UA----V----A----V----B----C---------O
- UA: user agent
- V: connect
- O: origin server
- ABC: 중개자
이 외에도 여러 프록시, 게이트웨이, 터널 등의 상황에 따라 체인은 변한다
:::
왜 HTTP/1.1은 중요한가
- 가장 많이 사용된다.
- REST의 중요한 특징인 통일 인터페이스, 무상태성, cache 등이 구현되어 있다
TCP/IP
HTTP/1.1는 네트워크 프로토콜인 TCP/IP를 기반으로 한다.
표로 표현하면
계층형 프로토콜 |
---|
애플이케이션층(HTTP) |
트랜스포트층(TCP) |
인터넷층(IP) |
네트워크 인터페이스층 |
- 네트워크 인터페이스층
- 물리적인 케이블, 어댑터등
- 인터넷층
- TCP/IP의 IP 담당
- 네트워크에서 데이터를 다루는 구간
- 기본 통신단위는 패킷
- 네트워크 인터페이스에서 수신하는 데이터만
보증
- 송신하는 데이터가 최종 도착점까지 잘 갔는지는
보증안함
- 트랜스포트층
- IP가 송신하는 데이터를
보증하는 곳
- 접속처의 상대를 컬렉션
- 이 컬렉션을 통해 데이터에 빠짐이 있는가 확인
- 포트는 1~65535이지만 기본값은
80
- IP가 송신하는 데이터를
- 애플리케이션층
- 자작한다면
socket
라이브러리를 사용하는 것이 보통. - 그러나 프레임워크 등을 사용하기에 신경쓸 일은 없음
- 대신 상세한 작동방식, 설정, 파라메터 등을 신경써야 함
- 자작한다면
전체상을 알아보자
HTTP의 구체적인 구조를 표로 정리하면 이렇다
클라이언트 | 서버 |
---|---|
request | response |
UA(RFC2616 정의상) |
UA: User Agent
request와 response
HTTP/1.1는 request-Response Style 프로토콜이다.
서버에서 처리가 길어지면 클라이언트는 대기한다. (동기형 프로토콜)
실제로 특정 사이트에 접속해보면
여담으로 github를 비롯한 유명한 사이트 대부분이 HTTP2였다...
request
GET /index.laf HTTP/1.1
Host: www.kyobobook.co.kr
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
DNT: 1
Upgrade-Insecure-Requests: 1
Connection: keep-alive
Cookie: WMONID=m8FSu3GwIhy; KYOBOSESSIONID=vXnNPhspRDdCdWLcHvHcPmQ0QRQh4TQFshqmmzpXnGJ1mRVtsQk5!1790412269!-2124087675!7200!-1!944508172!-2124087674!7200!-1; NSC_wjs_lzpcp_Dppljf=ffffffffd0b53b3045525d5f4f58455e445a4a42378b; PCID=16540732644003240091946
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-site
response
HTTP/1.1 200 OK
Date: Wed, 01 Jun 2022 08:48:07 GMT
Server: Apache
Content-Language: ko-KR
Keep-Alive: timeout=5, max=84
Connection: Keep-Alive
Content-Type: text/html; charset=EUC-KR
Set-Cookie: NSC_wjs_lzpcp_Dppljf=ffffffffd0b53b3045525d5f4f58455e445a4a42378b;expires=Wed, 01-Jun-2022 08:50:08 GMT;path=/;httponly
Cache-Control: private
Content-Encoding: gzip
Transfer-Encoding: chunked
클라이언트에서는 무슨 일이 일어났는가?
- Request 메세지 구축
- Request 메세지 송신
- (Response 대기)
- Response 메세지 수신
- Response 메세지 해석
- 클라이언트 목적 달성하기 위한 처리
- 일반적으로는 렌더링...
서버에서는 무슨 일이 일어났는가?
- (Request 대기)
- Request 메세지 수신
- Request 메세지 해석
- 적절한 애플리케이션 프로그램에 처리 위탁
- 애플리케이션 프로그램 결과 획득
- Response 메세지 구축
- Response 메세지 송신
메세지를 알아보자
Request와 Response 메세지를 하나모아 HTTP 메세지
라 한다.
Request 메세지
GET / HTTP/1.1
Host:kyobobook.co.kr
- 1행을 Request Line이라 한다.
- 메서드(GET), Request URI(/), 프로토콜 버전(HTTP/1.1) 구성이다.
Host
헤더는 필수.- 2행부터는 헤더
- 필요에 따라 바디가 등장.
Response 메세지
HTTP/1.1 200 OK
Content-Type: text/html; charset=EUC-KR
- 1행을 Status Line이라 한다.
- Status Line은 프로토콜 버전(HTTP/1.1), 상태코드(200), 텍스트(OK)로 구성된다.
- 2행 이후는 헤더이다.
- 경우에 따라 바디를 포함하기도 한다.
- 헤더와 바디는 빈줄(CRLF CRLF)로 구별한다.
:::note
HTTP/1.1 200 OK
Date: Sun, 10 Oct 2010 23:26:07 GMT
Server: Apache/2.2.8 (Ubuntu) mod_ssl/2.2.8 OpenSSL/0.9.8g
Last-Modified: Sun, 26 Sep 2010 22:04:35 GMT
ETag: "45b6-834-49130cc1182c0"
Accept-Ranges: bytes
Content-Length: 12
Connection: close
Content-Type: text/html
Hello world!
메세지 정리
이하 같은 표로 구성된다.
HTTP 메세지 |
---|
스타트라인(Request Line / Status Line) |
헤더 |
빈줄 |
바디 |
HTTP의 무상태성
서버가 클라이언트의 애플리케이션 상태를 저장하지 않는 제약
이것만으로는 설명이 부족하다!
유명한 햄버거 가게 예시를 보자.
Stateful
점원: 안녕하세요
고객: 안녕하세요
점원: 무엇을 주문하시겠어요?
고객: 햄버거세트요
점원: 마실 것은 뭘로 할까요?
고객: 콜라요
점원: 사이드메뉴는 뭘로 할까요?
고객: 프렌치 프라이요
점원: 더 필요하신 것은 없으신가요?
고객: 치즈스틱도 주세요
점원: 알겠습니다. 이것으로 주문을 다 마치신건가요?
고객: 네
점원: 알겠습니다
Stateless
고객: 안녕하세요
점원: 안녕하세요. 무엇을 주문하시겠어요?
고객: 햄버거세트요
점원: 알겠습니다. 마실 것은 뭘로 할까요?
고객: 햄버거 세트랑 콜라요
점원: 사이드메뉴는 뭘로 할까요?
고객: 햄버거 세트, 콜라랑 치즈스틱이요
점원: 더 필요하신건 없으세요?
고객: 햄버거 세트랑, 콜라랑, 치즈스틱 부탁드립니다
점원: 알겠습니다
정리
stateful | stateless | |
---|---|---|
장점 | 일상적 | 처리에 모든 정보가 담겨져있다 |
간결 | Self Descriptive Message로 클라이언트가 자신의 상태를 기억한다 | |
이에 서버 시스템 관리는 단순해진다(Request 메세지에 모든 정보가 담겨있으므로) | ||
단점 | 불특정 다수의 클라이언트를 처리하기에는 비용이 크다 | 길다 |
데이터 동기화할 때 오버헤드 위협이 크다 | 매회 새 주문 반복 | |
스케일아웃이 어렵다 | 송신할 데이터가 많아짐에 따른 퍼포먼스 저하 | |
인증 등 서버에 부하를 주는 처리를 반복해야함 | ||
대표 예시 | FTP | HTTP |
:::note 통신에러 처리 방법
Stateful
- 고객: 햄버거 세트 하나 주세요.
- 점원: 알겠습...(소음)
- 고객: (혹시 모르니) 햄버거 세트 하나 주세요
- 점원: 이미 햄버거 세트 하나가 주문되어 있습니다만... 햄버거 세트 하나 더 추가하겠습니다
이 때, 결과적으로는 상태가 있기에 햄버거 세트는 2개가 나온다.
Stateless
- 고객: 햄버거 세트 하나 주세요.
- 점원: 알겠습...(소음)
- 고객: (혹시 모르니) 햄버거 세트 하나 주세요
- 점원: 알겠습니다.
이 때는 상태가 없으므로 세트 하나만 주문된다. :::
애플리케이션 상태?
애플리케이션 상태 = 세션 상태
세션이란 로그인에서 로그아웃까지 상태를 지칭
참고로 세션과 늘 함께 언급되는 쿠키는 RFC6265에 정의됨
둘 다, 결국 무상태성을 지닌 HTTP의 상태 정보를 기억해주는 도구지만 동시에 개인정보를 위해 신중히 사용해야할 도구이다.
그래서 하고싶은 말은...
HTTP는 간결한 프로토콜이기에 강점이 있다.
메서드
8개 뿐이다!
메서드 | 의미 |
---|---|
GET | 리소스 조회 |
POST | 자식 리소스 작성, 리소스 데이터 추가, 그 외의 처리 |
PUT | 리소스 갱신, 리소스 작성 |
DELETE | 리소스 삭제 |
HEAD | 리소스 헤더(메타데이터) 조회 |
OPTIONS | 리소스가 지원하고 있는 메서드 조회 |
TRACE | 자신에게 리퀘스트 메세지를 반환(루프백) 테스트 |
CONNECT | 프록시 동작 터널 접속 변환 |
왜 이게 중요한가? 그 유명한 CRUD가 가능하다
CRUD?
CRUD명 | 의미 | 메서드 |
---|---|---|
Create | 작성 | POST/PUT |
Read | 읽기 | GET |
Update | 갱신 | PUT |
Delete | 삭제 | DELETE |
:::note
Web 성공 이유는 HTTP 메서드에 있다.
일반적인 프로그래밍 언어에 비해 HTTP는 8개 뿐이다.
하지만 이것은 REST의 통일 인터페이스 제약과 일맥상통하며, 메서드를 한정하고, 고정했기에 프로토콜이 단순성이 보증되었고 결과적으로 GET의 은닉된 안정성, PUT, DELETE의 멱등성(여러번 적용해도 결괏값이 달라지지 않는 것), POST의 만능성과 함께 Web의 성공으로 이어졌다.
:::
상태코드
HTTP는 Request <=> Response 프로토콜이다.
모든 Request에는 Response가 존재하다.
그리고 그 Response에는 의미있는 상태코드
가 존재한다.
상태코드는 중요한가?
시스템 밑그림을 그리는 역할을 한다.
예를들어 같은 4xx
상태코드라면, 401 Unauthorized
은 인증안됨 그러나 인증 가능, 403 Forbidden
은 지속적으로 거절이다.
만약 이 둘이 분류되지 않았다면 시스템이나 API에는 혼란이 있었을 것이다.
상태코드 톹아보기
분류 | 의미 |
---|---|
1xx | 처리중 |
2x | 성공 |
3xx | 리다이렉트 |
4xx | 클라이언트 에러 |
5xx | 서버 에러 |
:::note
4xx 계열은 401~417까지 정의되어있다 그러나 RFC2324에 왜인지 418 I'm a teapot
이라는 상태코드를 정의했다.
만약 이처럼 특수한 상태코드를 처리 못하는 경우는 어떻게될까? 바로 400 Bad Request
처리가 된다.
:::
핵심 상태코드
- 200 OK
- 201 Created
- 301 Moved Permanently
- 303 See other
- 400 Bad Request
- 401 Unauthorized
- 404 Not Found
- 500 Internal Server Error
- 503 Service unavilable
HTTP 헤더
헤더는 어디에 쓰일까?
- 메타데이터 표현을 하는 곳으로, 서버나 클라이언트는 헤더를 보고 메세지에 대한 처리를 결정한다.
- 캐시 같은 기능이 헤더에 구현되어 있다.
- 초기 HTTP/0.9에는 없었지만, RFC822 (나중에는 MIME(RFC 2821))
종류
- General header: 요청과 응답 모두에 적용되지만 바디에서 최종적으로 전송되는 데이터와는 관련이 없는 헤더.
- Request header: 페치될 리소스나 클라이언트 자체에 대한 자세한 정보를 포함하는 헤더.
- Response header: 위치 또는 서버 자체에 대한 정보(이름, 버전 등)와 같이 응답에 대한 부가적인 정보를 갖는 헤더.
특이한 헤더
엔티티 헤더
- 엔티티 헤더는 메시지 바디의 컨텐츠를 나타내는 HTTP 헤더
- 엔티티 헤더는 HTTP Request / Response 모두에 사용
- Content-Length, Content-Language, Content-Encoding과 같은 헤더는 엔티티 헤더
이하에서 Content-Length는 엔티티 헤더지만, Host와 User-Agent는 request 헤더
POST /myform.html 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
Content-Length: 128
- 이 헤더는 표준이 아님
- RFC7231에서 Representation header로 정의됨
업그레이드 헤더(RFC7230)
HTTP/1.1은 Upgrade 헤더필드를 통해 다른 프로토콜로 업그레이드할 수 있는 기능이 있다.
GET /index.html HTTP/1.1
Host: www.example.com
Connection: upgrade
Upgrade: example/1, foo/2
Connection: upgrade
Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
Connection: Upgrade
Upgrade: websocket
- HTTP/1.1 전용
- HTTP/2, HTTP/3에서는 사용할 수 없다
캐시
- 캐시는 서버에서 얻은 리소스를 로컬스토리지에 저장해 재이용하는 수법
- 서버에서 미리 특정한 리소스를 저장해서 재이용하는 수법
캐시?
만일 우리가 SNS에 연결했을 때
캐시가 없다면
- SNS 서버에 Request
- 서버에서 처리 후에 웹 페이지 Response
- Web 페이지 확인
캐시가 있다면
- SNS 서버에 request
- 특정조건(e.g 로그인이 안 되어있다)이면 처리 없이 바로 웹 페이지 Response
- Web 페이지 확인
지속적연결
- 파이프라이닝
- HTTP/1.1 당시 눈에 띄는 신기능으로, 이것이 기본값으로 동작
- 기존 HTTP/1.0에서는 비표준 Keep-Alive사용
- 연결을 끊고 싶다면 Request에서 Connection 헤더에
close
값 지정하면이 Request의 Response가 오면 연결을 끊는다
의미한다.
GET / HTTP/1.1
Host: example.com
Connection: close
맺는말
이 글을 쓰기 위해 약 2주 정도 조사하고 공부하면서, 여러 글과 영상을 보았지만 보고 메모하고 정리만 했지 이해가 되었는가 하면 여전히 이해하지 못했다.
그저 HTTP, HTTP1.1은 이런거구나.
그래서 REST 아키텍처 스타일이 나와 세상에 널리 퍼졌구나.
이런 느낌만 변했을 뿐이다.
시간이 흘러 다시 이 글을 보며,
아, 잘 못 정리했구나
아, 이런 소리구나
할 것이라 믿으며 메세지를 보면서 글을 마무리 짓자.
HTTP/1.1 200 OK
참조자료 및 읽을거리
- https://www.rfc-editor.org/rfc/rfc2616
- https://www.mnot.net/blog/2014/06/07/rfc2616_is_dead.html.brotli
- https://roka88.dev/111
- https://speakerdeck.com/iogi/atarasiihttpfalsehua-wosiyou
- https://tex2e.github.io/rfc-translater/html/rfc2616.html
- https://triple-underscore.github.io/RFC723X-ja.html
- https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=246391275
- https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=5928062
- https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=232268486
- https://www.amazon.co.jp/-/en/%E5%B1%B1%E6%9C%AC-%E9%99%BD%E5%B9%B3/dp/4774142042
- https://www.amazon.com/HTTP-Definitive-Guide-Guides/dp/1565925092
- https://www.amazon.com/HTTP-2-Action-Barry-Pollard/dp/1617295167
- https://qiita.com/mpyw/items/664390c945af6c035547
- https://qiita.com/katanov/items/1d99d27844392e380ec7
- https://qiita.com/Sekky0905/items/dff3d0da059d6f5bfabf
- https://qiita.com/kawasima/items/e48180041ace99842779
- https://qiita.com/mazgi/items/585348b6cdff3e320726
- https://qiita.com/koheiyamaguchi0203/items/5777c4653a01ae4c7b06
- https://zenn.dev/ak/articles/61d25099295372
- https://developer.mozilla.org/ko/docs/Web/HTTP
- https://datatracker.ietf.org/doc/
- https://www.w3.org/
- https://web.dev/performance-http2/
- https://evan-moon.github.io/2019/10/08/what-is-http3/
- https://blog.cloudflare.com/http3-the-past-present-and-future/
- https://infosec.mozilla.org/guidelines/web_security