웹과 HTTP 동작방식 - 비지속/지속 연결 HTTP, 버전별 특징
Web은 온디맨드 방식으로 동작한다. 온디맨드(On Demand) 방식이란 클라이언트 프로그램에서 요구를 하면 그 요구를 전달해 주는 것이다. 그래서 필요할 때만 리소스를 가지고 올 수 있다. 그리고 HTTP는 응용(Application) 계층 프로토콜이다. HTTP는 메시지의 구조 및 클라이언트와 서버가 메시지를 어떻게 교환하는지에 대해 정의하고 있다.
웹 브라우저는 HTTP의 클라이언트 측을 구현하고, 웹 서버는 HTTP의 서버 측을 구현한다. 웹 브라우저는 요구한 웹 페이지를 보여주고 여러 가지 인터넷 향해와 구성 특성을 제공한다. 또한 웹 서버는 URL로 각각을 지정할 수 있는 웹 객체를 가지고 있다. 따라서 HTTP는 웹 클라이언트가 웹 서버에게 웹 페이지를 어떻게 요청하는지와, 서버가 클라이언트로 어떻게 웹 페이지를 전송하는지를 정의한다.
📌 아래 글을 먼저 읽으면 도움이 될 것이다.
2024.04.15 - [Network] - [Network] TCP, UDP 란? - 차이점, 특성
비지속 연결 HTTP
HTTP/1.0
비지속 연결을 떠올리면 연결을 계속하지 않는다/끊는다 등의 느낌을 받을 수 있다. 느낌 그대로 비지속 연결 HTTP는 연결을 중간에 끊는다. 아래의 URL에 접속한다면 어떤 상황이 벌어질까?
http://www.school.edu/department/home.index
- HTTP 클라이언트는 HTTP의 기본 포트 번호인 80을 통해 http://www.school.edu 서버로 TCP 연결을 시도한다. 연결이 완료되면 클라이언트와 서버에 각각 소켓이 존재하게 된다.
- HTTP 클라이언트는 1단계에서 설정된 TCP 소켓을 통해 서버로 HTTP 요청 메시지를 보낸다. 이때 /department/home.index 경로를 포함하여 보낸다.
- HTTP 서버는 1단계에서 설정된 TCP 연결 소켓을 통해 요청 메시지를 받는다. 저장 장치로부터 /department/home.index 객체를 추출한다. HTTP 응답 메시지에 그 객체를 캡슐화한다. 그리고 응답 메시지를 소켓을 통해 클라이언트로 보낸다.
- HTTP 서버는 TCP에게 TCP 연결을 끊으라고 한다. 하지만 실제로 TCP 클라이언트가 응답 메시지를 올바로 받을 때까지 연결을 끊지 않는다.
- HTTP 클라이언트가 응답 메시지를 받으면 TCP 연결이 중단된다. 메시지는 캡슐화된 객체가 HTML 파일인 것을 나타낸다.
4단계에서 서버가 객체를 보고 클라이언트가 응답 메시지를 받으면 각 TCP 연결이 끊어지므로 비지속 연결을 사용하고 있는 것이다. 이것을 비지속 연결 HTTP라고 한다.
지속 연결 HTTP
비지속 연결은 매번 각 요청 객체에 대한 새로운 연결이 설정되고 유지되어야 한다는 단점이 있다. TCP 버퍼가 할당되어야 하고 TCP 변수들이 클라이언트와 서버 양쪽에 유지되어야 한다. 이것은 수많은 클라이언트들의 요청을 동시에 서비스해야 하는 웹 서버에 부담이 된다.
HTTP/1.1
✅ 재사용
HTTP/1.1은 서버가 응답을 보낸 후에 TCP 연결을 그대로 유지한다. 같은 클라이언트와 서버 간의 이후 요청과 응답은 같은 연결을 통해 보내진다. 그리고 객체에 대한 요구는 진행 중인 요구에 대한 응답을 기다리지 않고 연속해서 만들어질 수 있다.(파이프라이닝)
✅ 타임아웃
일반적으로 HTTP 서버는 일정 기간(타임아웃) 사용되지 않으면 연결을 닫는다.
HTTP/1.1은 한 번 TCP 연결이 되면 하나의 서버와 클라이언트가 하나의 TCP 연결 상에서 요청과 응답을 보낼 수 있다. keep-alive 헤더를 통해 연결을 재사용한다. 헤더를 확인하고, 해당 헤더를 통해 TCP 연결을 유지한다.
그런데 하나의 TCP 상에서 웹 페이지에 있는 모든 객체를 보내면 HOL(Head of Line) 블로킹 문제가 발생할 수 있다. HOL 블로킹이란, 만약에 한 웹 페이지 상단에 비디오 클립이 있다고 가정하면 하단에 위치한 작은 객체들은 비디오 클립 뒤에서 기다려야 하는 시간이 길어진다. 즉 비디오 클립이 뒤에 오는 작은 객체들을 블로킹하게 되는 현상이다.
HTTP/1.1 브라우저에서는 여러 개의 병렬 TCP 연결을 열어서 HOL 블로킹 문제를 해결하려고 했다. 여러 개의 병렬 TCP 연결이 열리면, 특정 연결이 네트워크 자원을 독점하지 않도록 한다. 웹 페이지에 접속했을 때 텍스트가 먼저 로딩되고 비디오나 사진은 나중에 로딩되는 것이 이 블로킹을 해결한 모습이다.
⚠️ 잠깐 보충 설명 About 쿠키 & 웹 캐시
쿠키 🍪
우리가 인터넷 쇼핑을 할 때 내가 지난번 장바구니에 담아둔 옷이 그대로 담겨있는 현상을 본 적이 있을 것이다. 이것은 웹 사이트가 사용자를 확인할 수 있었기 때문이다. HTTP 쿠키는 사이트가 사용자를 추적하게 해 준다.
쿠키의 기술은 네 가지다.
- HTTP 응답 메시지 쿠키 헤더 라인
- HTTP 요청 메시지 쿠키 헤더 라인
- 사용자의 브라우저에 사용자 엔드 시스템과 관리를 지속시키는 쿠키 파일
- 웹 사이트의 백엔드 데이터베이스
예시 시나리오를 통해 이해해 보자.
- 네이버 웹 서버에 요청이 들어올 때 그 서버는 유일한 식별번호를 만들고 이 식별번호로 인덱싱 되는 백엔드 데이터베이스 안에 엔트리를 만든다. 그리고 네이버 웹 서버는 응답 메시지에 식별 번호를 담고 있는 Set-Cookie: 헤더를 포함시킨다. Set-Cookie: 1623
- 은서가 응답 메시지를 받으면 브라우저는 관리하는 특정한 쿠키 파일에 그 라인을 덧붙인다. 그리고 다시 웹 페이지에 요청을 할 때 은서의 브라우저는 쿠키 파일을 참조하고 이 사이트에 대한 은서의 식별번호를 읽어서 HTTP 요청에 식별번호를 포함하는 쿠키 헤더 파일을 넣는다. Cookie: 1623
- 네이버 웹 사이트는 은서의 이름을 몰라도 1623 사용자가 어는 페이지를 어떤 순서로 몇 시에 방문했는지 정확히 알 수 있다.
웹 캐시(= 프록시 서버) 💽
캐시라는 것은 데이터베이스에 접근하지 않고 이미 사용된 데이터를 저장하여 속도와 성능면에서 우수할 수 있도록 한다. 웹 캐시도 마찬가지로 자체의 저장 디스크를 가지고 있기 때문에 최근에 사용된 객체의 사본을 저장한다. 그래서 모든 HTTP의 요구가 웹 캐시에 가장 먼저 보내지도록 한다.
예시 시나리오를 통해 이해해 보자
http://www.school.edu/department/home.index
- 브라우저는 웹 캐시와 TCP 연결을 설정하고 브라우저가 웹 캐시에 있는 객체에 대한 HTTP 요청을 보낸다.
- 웹 캐시는 객체의 사본이 자신에게 저장되어 있는지 확인한다. 만약 저장되어 있다면 웹 캐시는 클라이언트 브라우저로 HTTP 응답 메시지와 함께 객체를 전송한다.
- 만약 저장되어 있지 않다면, 웹 캐시는 기점 서버인 http://www.school.edu로 TCP 연결을 설정한다. 그리고 웹 캐시는 서버로 HTTP 요청을 보낸다. 이 요청을 받은 서버는 웹 캐시로 HTTP 응답 메시지와 함께 객체를 보낸다.
- 객체를 수신할 때, 객체를 지역 저장장치에 복사하고 클라이언트 브라우저에 HTTP 응답 메시지와 함께 객체의 사본을 전달한다.
이 경우는 캐시가 서버이면서 클라이언트라고 할 수 있다. 그런데 하나의 문제점이 있다. 캐시의 고전적인 문제인데, 캐시에 저장된 객체의 복사본이 새 값이 아닐 수 있다는 것이다. 그래서 조건부 GET이라는 개념이 등장한다.
조건부 GET
HTTP는 클라이언트가 브라우저로 전달되는 모든 객체가 최신이라는 것을 확인하면서 캐싱하게 해주는 방식을 가지고 있다. 이러한 방식을 조건부 GET이라고 한다.
- 브라우저의 요청을 대신하여 프록시 서버는 요청 메시지를 웹 서버로 보낸다.
- 웹 서버는 프록시 서버에게 객체를 담은 응답 메시지를 보낸다.
- 캐시는 요청한 브라우저에게 객체를 보내주고 자신에게도 객체를 저장한다. 이때 객체와 함께 마지막으로 수정된 날짜를 함께 저장한다.
- 몇 주 후에 다른 브라우저가 같은 객체를 캐시에게 요청하면 객체는 여전히 저장되어 있으므로 브라우저는 조건부 GET으로 갱신 여부를 조사한다.
- 이때 조건부 GET은 서버에게 그 객체가 명시된 날짜 이후 수정된 경우에만 그 객체를 보내라고 말한다.
HTTP/2
✅ HOLB 문제 해결
한 개의 TCP 연결을 통해 여러 요청과 응답을 동시에 처리한다.
✅ 프레임 기반 전송 : Frame으로 쪼개서 Round-Robin 방식을 사용한다. 프레임 인터리빙(Frame Interleaving)방식을 사용해서 큰 데이터와 작은 데이터를 번갈아가면 전송한다.
✅ 헤더 압축
HPACK 압축 알고리즘을 사용하여 헤더 크기를 줄인다. 중복 데이터를 최소화하니 전송 속도가 향상된다.
✅ 서버 푸시
클라이언트가 요청하기 전에 서버가 미리 데이터를 전송한다.
HTTP/1.1에서는 여러 개의 병렬 TCP 연결을 열어서 HOL을 해결하고자 했다. 하지만 이는 부하를 만들어낸다. HTTP/2에서는 하나의 TCP 연결에 여러 개의 스트림이 존재하고 동시에 요청과 응답을 보낼 수 있다.
예를 들어, 하나의 큰 비디오 클립과 8개의 작은 객체가 있다고 한다.
비디오 클립 : 1000개의 프레임
작은 객체들 : 2개의 프레임
서버는 이 웹 페이지를 확인하기 위해 9개의 병렬 요청을 받게 된다. 그리고 서버는 9개의 HTTP 응답 메시지를 브라우저로 보내야 한다.
인터리빙을 사용한다면,
비디오 클립의 한 프레임 전송, 소형 객체의 첫 번째 프레임 전송, 비디오 클립의 한 프레임 전송, 소형 객체의 첫 번째 프레임 전송, ... 이렇게 반복되기 때문에 모든 소형 객체는 18개의 프레임을 보낸 후에 전송된다.
인터리빙을 사용하지않는 HTTP/1.1 방식이라면
그런데 만약 인터리빙이 사용되지 않았다면 모든 소형 객체들은 1016(1000+2*8) 개의 프레임을 보낸 후에 전송되는 것이다.
HTTP/3
✅ QUIC
HTTP/3는 QUIC 위에서 작동되도록 설계된 새로운 HTTP 프로토콜이다. UDP 프로토콜 위에 위치하는 응용계층에 구현되어 있다.
✅ 멀티플렉싱
스트림을 독립적으로 처리할 수 있다.
HTTP/2는 TCP 기반으로 동작하기 때문에 여전히 HOL블로킹을 완벽하게 해결할 수 없다. 하나의 TCP 연결에서 여러 스트림을 처리할 수 있었지만 스트림 중 하나라도 패킷 손실이 발생하면 전체 스트림이 대기해야 한다. 결국 TCP 위에서는 완벽히 해결할 수가 없는 것이다.
HTTP/3은 UDP 기반 QUIC 프로토콜을 사용해서 이 한계를 해결했다. HTTP/3에서는 QUIC의 멀티플렉싱을 통해 스트림이 독립적으로 처리되므로, 패킷 손실이 발생해도 나머지 스트림에는 영향을 주지 않는다.
TLS 1.3을 내장하여 보안을 강화했다. HTTP/2는 TLS handshake가 필요하지만 HTTP/3은 QUIC 내부에서 보안이 적용된다. 대신 모든 HTTP/3 연결은 암호화가 필수다.