[Computer Network] HTTP
HTTP/0.9
CSS JS 없이 HTML 문서 전송용으로만 설계된 버전으로 헤더 개념도 없었다.
GET /index.html 한 줄짜리 명령어만 전송했고 메서드도 GET 하나 뿐.
응답 역시 HTML 본문만 전송하고 응답이 끝나면 TCP 연결을 종료한다.
HTTP/1.0
HTML IMAGE CSS JS 등 다양한 리소스를 요청할 수 있다.
HTTP/0.9는 확장이 불가능해 헤더와 MIME타입 상태코드를 추가한 버전인 1.0 버전을 도입함.
요청에는 요청 라인 + 헤더 + 본문 구조가 정립됐다.
응답에는 상태 라인 + 헤더 + 본문 구조가 정립됐다.
Content-Type 식별을 위해 MIME 시스템을 도입했고, 클라이언트가 받을 수 있는 타입을 명시해준다.
다만 매 요청마다 TCP연결을 새로 맺어야 했기에 페이지 로딩이 느리고 오버헤드가 크다.
HTML 문서 하나를 요청할 때 마다 TCP 연결을 새로 맺는데.. 페이지 하나는 HTML 하나로만 이루어지는게 아니다.
이미지 CSS JS 등.. 여러 리소스를 가져와야 한다. 한 페이지를 로딩하는 동안 수십 개의 TCP 연결이 만들어진다.
TCP 연결은 또 3-way handshake 과정을 통해 만들어지니 연결을 시작하는 데에만 1RTT가 걸리고..
또 HTTP 요청을 보내고 응답을 받는데 또 1RTT가 걸린다. (총 2RTT - Round Trip Time)
사용자가 페이지를 요청하고 그 페이지가 화면에 렌더링 될 때 까지 걸리는 총 시간을 PLT라고 부른다.
PLT = DNS 조회 + TCP 연결 + HTTP 요청/응답 + 리소스 다운로드 + 렌더링
1.0에서는 데이터를 얼마나 빠르게 전송하는지보다 얼마나 자주 기다리는지가 더 중요하다.
한 번에 많은 데이터를 보낼 수 있어도 TCP handshake 로 걸리는 시간이 훨씬 크다.
HTTP/1.1
1.0 버전의 RTT 병목 구조를 근본적으로 해결한다.
다음 버전이 등장하기 전까지 거의 20년간 사용된 표준 버전으로 현대 웹의 기반을 세운 버전이다.
Persistent Connection
기존에는 객체마다 새 TCP를 연결했지만 이제는 한 번만 연결하고 여러 객체를 순차적으로 주고받는다.
Connection: keep-alive 값이 기본 값으로 사용돼 하나의 TCP 연결 안에서 여러 요청을 처리한다.
당연히 handshake도 한 번만 하게 되니 PLT도 크게 감소한다.
Pipelining
요청을 전송할 때, 응답을 기다리지 않고 연속해서 전송한다.
서버는 순차적으로 응답해야 하니 Head-Of-Line Blocking 문제가 발생하긴 한다. 다만 요청은 RTT 순차 대기 없이 겹쳐진다.
이론적으로는 RTT 절감 효과가 뛰어났지만, 프로토콜 설계의 구조적 한계 때문에 실제 웹 브라우저에서는 거의 구현되지 않았다.
Domain Sharding
Persistent Connection을 도입했지만 브라우저는 서버로 너무 많은 연결을 동시에 여는걸 제한한다.
크롬은 최대 동시 연결 수를 6개로 제한하는데.. 이러면 한 번에 최대 6개의 TCP 연결만 가능하다.
6개 이상의 리소스를 받아야 하면 6개씩 묶어서 직렬처리가 발생하는데.. 기껏 줄여둔 PLT가 다시 늘어난다.
그래서 Domain Sharding을 사용한다. 하나의 리소스를 여러 가짜 도메인으로 분산시켜 도메인당 연결 제한을 우회한다.
이미지 1~10 www.example.com -> img1.example.com
이미지 11~20 www.example.com -> img2.example.com
JS/CSS www.example.com -> static.example.com
그런데 이러면 DNS Lookup이 증가해 도메인마다 별도 DNS 쿼리를 보내야 하고
도메인마다 새로운 TCP 세션을 생성해야 하니 handshake 시간도 늘어난다.
서로 다른 서브도메인이니 쿠키 동기화도 어렵다.
당연히 서버 담당자가 서브도메인을 만들고 그 도메인에 대한 처리 로직도 작성해 줘야 하고.. 좀 귀찮은 부분이 많다.
HTTP/2
Pipelining, Domain Sharding 문제를 한 번에 해결한 버전인 HTTP/2가 2015년에 출시됐다.
1.1버전이 RTT를 줄이는 트릭을 구현했다면 2버전에서는 전송 방식을 완전히 새로 설계했다.
Keep-Alive Pipelining Sharding 백날 해봐도..
TCP 연결 중복, 헤더 중복, RTT 낭비, HOLB 문제는 해결되지 않는다.
구글은 이 문제를 해결하기 위해 SPDY를 만들었고, SPDY를 기반으로 HTTP/2 버전이 도입됐다.
단일 TCP 연결에서 다중 요청을 병렬 처리하는걸 목표로, 하나의 연결을 다중 stream으로 쪼개서 병렬화한다.
즉, 데이터를 프레임 단위로 쪼개서 전송한다. (Multiplexing)
+-------------------------------------------+
| Stream 1: HEADERS frame |
| Stream 1: DATA frame (HTML Body part 1) |
| Stream 2: HEADERS frame |
| Stream 2: DATA frame (CSS) |
| Stream 1: DATA frame (HTML Body part 2) |
| Stream 3: HEADERS frame |
| Stream 3: DATA frame (Image) |
+-------------------------------------------+
하나의 TCP 연결 안에서 여러 Stream이 번갈아가면서 전송되고, 서버는 각 프레임의 Stream ID로 어떤 요청에 속한 데이터인지 구분한다.
Apache, Nginx, Node.js, Jetty 는 HTTP/2 를 직접 지원하는 소프트웨어.
직접 코드를 작성하지 않아도 이렇게 대부분의 서버들이 이미 HTTP/2 프로토콜을 구현해두었기에.. 설정만 하면 HTTP/2로 통신 가능함.
HTTP/3
이번에는 지금까지 써오던 TCP를 버리고 새로운 전송 프로토콜인 QUIC를 채택했다.
HTTP/2에서도 응용 계층에서 멀티플렉싱을 사용해 병렬화는 잘 됐는데
모바일 환경에서 와이파이에서 LTE로 IP가 바뀌면 TCP 연결이 끊어지고..
일단 TCP를 사용하다 보니 handshake 탓에 연결 할 때 마다 RTT만큼 기다려야 하고..
패킷 손실 시 재전송될 때 까지 이후 데이터 전체를 지연시키는 문제가 있었다.
그냥 TCP 얘가 병목임;; 그래서 구글은 QUIC를 만들고 이걸 HTTP/3에 적용함.
QUIC는 UDP 위에서 TCP + TLS + HTTP/2 기능을 통합해서 다시 구현한 전송 계층 프로토콜이다.
그냥 TCP의 기능을 다시 구현한거임.. 연결 설정 / 혼잡 제어 / 에러 제어 등 모든 TCP 기능을 다시 구현..
다만 TCP는 운영체제에 구현되어있지만 QUIC는 사용자 공간에서 구현된다. 즉, 전송계층 기능을 응용계층으로 이동시킨 것.
여기서 사용자 공간은 브라우저와 서버를 의미한다.
크롬은 구글꺼니까 HTTP/3 당연히 지원하는데, Nginx 등 대상 서버도 HTTP/3을 지원해야지 서로 QUIC 기반으로 통신할 수 있다.
Nginx 기준으로.. HTTP/3은 1.25버전 이후부터 지원된다.
대상 서버가 HTTP/3을 지원하지 않으면? 자동으로 HTTP/2나 그 아래 버전으로 다운그레이드 된 상태로 통신된다.
HTTP는 하위 호환이 잘 설계됐으니.. 서버가 최신 프로토콜을 지원하지 않아도 통신 자체는 절대 끊기지 않는다.
사실 Tomcat같은 전통 WAS는 HTTP/3을 지원하진 않는다.
그런데 큰 문제는 없다. Nginx같은 웹 서버를 리버스 프록시로 두고, 리버스 프록시에서 Tomcat으로 연결해주면 된다.
당연히~ 리버스 프록시와 Tomcat 에서는 HTTP/3으로 통신할 수 없지만 이건 문제되지 않는다.
어차피 대부분의 병목은 브라우저와 리버스 프록시 사이에서만 발생한다.
저 구간에서만 네트워크 RTT가 크고, handshake 초기 연결 비용이 발생하니 HTTP/3으로 병목을 해결해 줘야 하고
리버스 프록시와 WAS 사이는 RTT도 짧고 손실도 거의 없어 TCP로도 충분히 안정적이다.
이전에는 TLS가 TCP 위에서 따로 작동했지만 3버전부터는 UDP 기반의 QUIC 위에서 작동한다.
즉, QUIC 프로토콜 안에 TLS 1.3이 통합되어있어 패킷 내부에서 암호화와 연결 설정을 동시에 처리한다.
당연한 말이지만 HTTP/3 을 쓰던 HTTP/1.1 을 쓰던 스프링 프레임워크를 사용해서 백엔드 서버를 구축하는 백엔드 개발자는 HTTP 버전에 전혀 상관하지 않고 코드를 작성한다.
웹 프레임워크는 HTTP를 직접 구현하지 않는다. 그냥 HTTP 요청과 응답을 처리하는 인터페이스만 추상화한다.
Tomcat Nginx같은 실제로 HTTP 전송 로직을 담당하는 서버를 조작해야 한다.
WebSocket
HTTP 위에서 실시간 통신을 가능하게 한 프로토콜으로, 클라이언트와 서버가 지속적으로 연결된 하나의 TCP 채널으로 양방향으로 데이터를 주고받도록 도와주는 프로토콜이다.
채팅방을 HTTP로 구현하면 클라는 서버에게 주기적으로 Polling 해야하는데 이게 너무 비효율적임.
그래서 WebSocket을 사용한다. HTTP 연결 한 번으로, TCP 연결을 끊지 않고 그대로 유지한다.
그런데 HTTP/3은 TCP를 버리고 UDP를 쓰는데? 그럼 TCP 기반인 웹소켓은 어떻게 쓰는거지?
기존 웹소켓은 HTTP/3 위에서 동작할 수 없다. 2025 기준 브라우저에서 웹소켓 쓰면 HTTP/3 버전으로 페이지 열었다고 해도 실제 연결은 다운그레이드돼서 TCP 기반 HTTP로 전환된다.
그래서 등장한게 WebSocket over QUIC와 WebTransport.
'Computer Science > Computer Network' 카테고리의 다른 글
| [Computer Network] Domain Name System (0) | 2025.10.14 |
|---|---|
| [Computer Network] Cookie && Cache (2) | 2025.10.14 |
| [Computer Network] Internet Protocol Layer (3) | 2025.10.14 |
| [Data Communication] Point to Point Protocol / 3G (0) | 2025.06.17 |
| [Data Communication] Media Access Control과 Ethernet (4) | 2025.06.12 |
댓글
이 글 공유하기
다른 글
-
[Computer Network] Domain Name System
[Computer Network] Domain Name System
2025.10.14 -
[Computer Network] Cookie && Cache
[Computer Network] Cookie && Cache
2025.10.14 -
[Computer Network] Internet Protocol Layer
[Computer Network] Internet Protocol Layer
2025.10.14 -
[Data Communication] Point to Point Protocol / 3G
[Data Communication] Point to Point Protocol / 3G
2025.06.17