HTTP(HyperText Transfer Protocol)
HTTP는 애플리케이션 계층에서 웹 서비스 통신에 사용되는 프로토콜이다.
HTTP는 HTML 문서와 같은 리소스들을 가져올 수 있도록 해주는 프로토콜로 웹에서 이루어지는 모든 데이터 교환의 기초이며 클라이언트-서버 프로토콜이기도 하다.
클라이언트-서버 프로토콜이란 클라이언트 및 서버 요구 사항을 기반으로 하는 요청 및 응답 프로토콜로 서로 호환된다면 모든 유형의 콘텐츠를 교환할 수 있다.
HTTP 요청
HTTP 요청은 웹 브라우저와 같은 인터넷 통신 플랫폼에서 웹 사이트를 로드하는 데 필요한 정보를 요청하는 방법이다.
인터넷을 통해 이루어진 각 HTTP 요청은 서로 다른 유형의 정보를 전달하는 인코딩 된 데이터를 전달하게 된다.
일반적으로 아래와 같은 정보를 담아서 HTTP 요청을 보내게 된다.
- HTTP 버전 유형
- URL
- HTTP 메서드
- HTTP 요청 헤더
- 요청 메시지
- HTTP 메서드
HTTP 메서드는 HTTP 요청이 쿼리된 서버에서 기대하는 작업을 나타낸다.
- GET
- POST
- PUT
- DELETE
- (....)
- HTTP 요청 헤더
HTTP 헤더는 키-값 쌍으로 이루어진 텍스트 정보가 포함되어 있으며 모든 HTTP 요청에는 HTTP 헤더가 포함된다.
헤더에는 클라이언트가 사용하는 브라우저 및 요청되는 데이터와 같은 핵심 정보를 전달한다.
HTTP 응답
HTTP 응답은 웹 클라이언트에서 HTTP 요청에 대한 응답으로 인터넷 서버로부터 수신하는 응답이다.
일반적인 HTTP 응답에는 아래와 같은 정보가 포함된다.
- HTTP 상태 코드
- HTTP 응답 헤더
- 응답 메시지
- HTTP 상태 코드
HTTP 상태 코드는 HTTP 요청이 성공적으로 완료되었는지 여부를 나타내는 데 가장 자주 사용되는 3자리 코드이다.
- 1XX Informational
- 2XX 성공
- 3XX Redirection
- 4XX 클라이언트 오류
- 5XX 서버 에러
자세한 HTTP 상태 코드는 아래의 공식 문서를 참고해 보자.
- HTTP 응답 헤더
HTTP 응답 헤더는 응답 본문에서 전송되는 데이터의 언어 및 형식과 같은 중요한 정보를 전달하는 헤더가 함께 제공된다.
- HTTP 응답 메시지
응답 메시지는 HTTP 응답의 마지막 부분에 들어가는데 모든 응답에 메시지가 있는 것은 아니다.
일반적으로 HTTP 응답으로 html 데이터를 보내주게 되고, 만약 201(Created), 204(No Content)와 같은 상태 코드를 가진다면 응답에 메시지를 포함하지 않는다.
HTTP의 특성
- Stateless(무상태)
HTTP는 무상태(Stateless)라는 특징을 가지고 있는데 통신이 끝나면 더 이상 상태(데이터)를 유지하지 않는다는 특징이다.
해당 특징으로 인해서 여러 가지 문제가 발생할 수 있는데 대표적인 예시를 들자면
웹 사이트에 로그인을 했는데 다른 페이지로 이동할 때마다 로그인 상태가 유지되지 않아서 매번 로그인을 다시 해줘야 한다.
지겹게 뜨는 광고 팝업을 보지 않으려고 하루 동안 보지 않기를 클릭하고 껐는데 다시 해당 페이지로 오면 광고 팝업이 뜬다.
이러한 문제들이 발생하여 HTTP 통신 중 발생하는 상태 즉, 데이터를 어딘가에 저장해야 하는데 이때 사용하는 기술이 쿠키와 세션 혹은 토큰이다.
- Connectionless(비연결성)
HTTP는 기본적으로 TCP/IP를 기반으로 통신하게 되는데 이러한 TCP/IP 특성으로 인해서 연결을 종료하지 않으면 계속 유지되는 문제가 발생한다.
만약 여러 클라이언트가 하나의 서버에 접속했다고 가정하면 TCP 연결이 끊어지지 않고 계속 서버의 자원을 소비하는 문제가 발생할 것이다.
따라서 이러한 문제를 해결하기 위해 클라이언트가 서버에 요청을 하고 응답을 받으면 바로 TCP/IP 연결을 끊도록 하여 비연결성(Connectionless)의 특징을 보였다.
하지만 비연결성 방식에도 문제가 발생하였는데 기본적으로 TCP/IP는 연결을 하기 위해서 3-way handshake 과정을 거치게 되므로 HTTP 요청마다 해당 과정을 거치게 되면서 많은 오버헤드가 발생하였고 그로 인한 자원 낭비와 성능 저하가 발생하였다.
해당 문제를 해결하기 위해 HTTP 1.1 버전부터 지속 연결(Persistence Connection) 방식을 사용하여 TCP 연결을 재사용하는 방식을 채택하게 되었다.
HTTP/1.0
초기의 HTTP/1.0 버전은 한 연결당 하나의 요청을 처리하도록 설계되었는데(Connectionless) 서버로부터 파일을 가져올 때마다 TCP의 3-웨이 핸드셰이크를 계속 열어야 하기 때문에 RTT가 증가하는 문제가 발생한다.
RTT 증가로 인해 서버에 부담이 많이 가고 사용자 응답 시간이 길어지게 된다는 문제점이 발생한다.
이러한 문제를 해결하기 위해 여러 가지 방법이 있는데 핵심은 한 번의 연결에 많은 데이터를 보내는 것이다.
RTT: 패킷이 목적지에 도달하고 나서 다시 출발지로 돌아오기까지 걸리는 시간 즉, 패킷 왕복 시간
Image Sprite(Image Splitting)
많은 이미지를 다운로드하게 되면 과부하가 걸리기 때문에 많은 이미지가 합쳐 있는 하나의 이미지를 다운로드하고 background-image의 position을 이용하여 이미지를 표기하는 방법이 있다.
서버에 요청하는 수가 줄어들어 로딩 시간이나 오버헤드를 줄일 수 있지만 원하는 이미지를 사용하기 위해 정확한 position 값을 알아야 하고, 특정 이미지를 변경하기 위해서는 단일 이미지 파일보다 수정하기 어렵다는 단점을 가지고 있다.
코드 압축
코드를 압축해서 개행 문자, 빈칸을 없애서 코드의 크기를 최소화하는 방법이 있다.
이미지 Base64 인코딩
이미지 파일을 64진법으로 이루어진 문자열로 인코딩하는 방법이다. 해당 방법을 사용하면 서버와의 연결을 열고 이미지에 대해 서버에 HTTP 요청을 할 필요가 없다는 장점이 있다.
하지만 반대로 Base64 문자열로 변환할 경우 37% 정도 크기가 더 커지는 단점이 있다.
인코딩: 정보의 형태나 형식을 표준화, 보안, 처리 속도 향상, 저장 공간 절약 등을 위해 다른 형태나 형식으로 변환하는 처리 방식
HTTP/1.1(Persistent Connections)
HTTP/1.1은 HTTP/1.0에서 발전한 방식으로 한 번 TCP 초기화를 한 이후 keep-alive라는 옵션으로 여러 개의 파일을 송수신할 수 있게 바꾼 방식이다.
한 번의 TCP 3-웨이 핸드셰이크가 발생하면 그다음부터는 발생하지 않는다. 하지만 문서 안에 포함된 다수의 리소스(이미지, css 파일, script 파일 등)를 처리하려면 요청할 리소스 개수에 비례해서 대기 시간이 길어지고 이로 인해 서버에 부하가 생기기 때문에 keep-alive 옵션을 통해서 연결 유지 시간을 제한하고 있다.
또 다른 특징으로는 파이프라이닝(Pipelining)이다.
파이프라이닝은 클라이언트가 여러 요청을 연달아 보내야 할 때 각 응답을 기다리는 것이 아닌 발생한 요청은 일단 서버에 전송하는 방식이다.
이전의 HTTP/1.0 버전을 보면 여러 요청이 있어도 하나의 요청과 응답 밖에 하지 못했고, 그로 인한 지연이 발생하게 되었다. 하지만 HTTP/1.1 버전부터 파이프라이닝 기법을 사용하게 되면서 여러 요청을 순차적으로 보내면, 서버는 받는 순서대로 응답을 제공할 수 있게 되면서 지연을 개선할 수 있었다.
하지만 이러한 HTTP/1.1 버전에도 2가지 문제가 있었는데 무거운 헤더 구조를 가지고 있는 것과 HOL Blocking 문제가 있었다.
HOL Blocking(Head Of Line Blocking)
HOL Blocking은 네트워크에서 같은 큐에 있는 패킷이 그 첫 번째 패킷에 의해 지연될 때 발생하는 성능 저하 현상을 말한다.
HTTP/1.1은 응답 다중화(Multiplexing)를 할 수 없어서 받아온 요청의 순서대로 처리돼야 하는데 앞서 설명했던 것처럼 여러 리소스가 포함된 파일이 느리게 받아진다면 그 뒤에 있는 것들이 대기하게 되며 다운로드가 지연되는 상태가 발생할 수 있다.
무거운 헤더 구조
기존의 HTTP/1.0 버전에서 HTTP/1.1 버전으로 개선되면서 다양한 기능이 추가되었고, 이로 인해 HTTP/1.1의 헤더에는 쿠키와 같은 많은 메타데이터가 담기게 되었다.
헤더에 많은 데이터가 담기면서 점점 무거워지고, 매번 요청할 때마다 헤더를 중복해서 전송해야 되기 때문에 많은 낭비가 발생하게 되었다.
HTTP/2
웹 환경이 계속 확장되고 더욱더 복잡해지면서 HTTP/1.1이 가지는 문제점들이 발생하기 시작했다.
웹 서비스라고 하면 가장 먼저 떠오르는 구글이 데이터 전송 지연 시간을 줄이고 응답 시간을 더 빠르게 하기 위해서 SPDY라는 새로운 프로토콜을 구현하게 되면서 HTTP/2의 초안이 탄생하게 되었다.
사진을 보면 SPDY는 TLS(Transport Layer Security) 위에서 동작하는 것을 알 수 있는데 이것을 통해 알 수 있는 것은 보안 기능이 없는 일반적인 HTTP에서는 적용할 수 없다는 뜻이다. 따라서 기본적으로 HTTP/2는 HTTPS로 작성된 웹 사이트만 적용이 가능하다.
HTTP/2 특징
- Binary Framing Layer
기존의 HTTP/1.1은 HTTP 메시지가 아스키코드로 작성되는 텍스트 기반 프로토콜이었다. 그로 인해서 사람들이 읽기는 편했지만 불필요하게 데이터가 커지는 문제가 있었다.
HTTP/2로 와서는 HTTP 메시지가 텍스트가 아닌 binary frame으로 인코딩 되어 전송하여 훨씬 효율적으로 데이터를 전송할 수 있게 되었다.
HTTP/2에서 사용하는 단위로 메시지를 제외하고 스트림과, 프레임이 추가되었다.
- Stream: 요청과 응답이 양방향으로 오가는 논리적 연결 단위로 하나의 Connection 내에서 양방향으로 메시지를 주고받는 하나의 흐름이다.
- Message: 하나의 요청과 응답을 구성하는 단위로 다수의 Frame으로 이루어진 배열이다.
- Frame: 메시지를 구성하는 최소 단위로 HTTP 헤더 혹은 데이터가 들어있고, 서버로 전송할 때는 잘게 쪼개어 전송되기 때문에 수신 측에서 다시 조립해서 사용한다.
위의 그림을 보면 DATA와 HEADERS가 Frame 단위인 것을 알 수 있는데 이처럼 메시지를 Frame 단위로 나눠서 전송하게 되고 이러한 Frame을 모아서 조합하면 요청이나 응답 메시지가 되는 것이다.
또한 하나의 커넥션에서 여러 스트림들이 존재하게 되어 병렬적으로 처리되기 때문에 속도가 이전 HTTP/1.1보다 확연히 빠를 수밖에 없다.
- Multiplexing(응답 다중화)
이전에 HTTP/1.1은 TCP 연결에서 한 번에 하나의 요청만 처리 가능하며 파이프라이닝을 통해 요청 별 순서를 반드시 지켜야 했지만 HTTP/2로 와서는 하나의 TCP 연결에서 여러 요청을 동시에 처리할 수 있게 되었다.
이것이 가능했던 이유는 TCP 연결을 스트림, 메시지, 프레임이라는 단위로 세분화하여 처리했기 때문에 가능했다.
멀티 플렉싱은 여러 개의 스트림을 사용하여 송수신한다는 것을 의미한다.
위의 그림을 보면 전송되는 스트림이 각각 독립적으로 구분되어 있는 것을 알 수 있는데 이를 통해 특정 스트림의 패킷이 손실되었다고 하더라도 해당 스트림에만 영향을 미치고 나머지 스트림에 영향을 주지 않게 된다.
멀티 플렉싱을 통해서 애플리케이션에서 받아온 메시지를 독립된 프레임으로 조각내여 서로 송수신한 이후 다시 조립하며 데이터를 주고받을 수 있게 되면서 HOL Blocking을 해결할 수 있다.
- HTTP/1.1과 HTTP/2의 통신 과정 비교
위의 예시를 보면 HTTP/1.1은 하나의 TCP 연결을 맺고 요청을 보내 응답이 도착해야 해당 연결을 재사용하면서 다시 요청을 보낼 수 있었다.
웹 브라우저는 지연 시간을 줄이기 위해서 여러 개의 TCP 연결을 만들어 여러 개의 요청을 보내는 방법을 사용했지만 최근 웹 서비스들은 엄청난 양의 요청이 필요해지면서 TCP 연결을 늘리는데 한계가 있었다.
하지만 HTTP/2에서는 하나의 TCP 연결에서 여러 개의 스트림이 동시에 요청 및 응답을 하는 방식으로 병렬적으로 처리가 가능해지면서 앞서 HTTP/1.1의 한계를 해결할 수 있게 되었다.
- 헤더 압축
허프만 코딩 압축 알고리즘을 사용하는 HPACK 압축 형식을 가진다.
허프만 코딩은 달라진 부분만 다시 전송하고 달라지지 않은 부분은 전송하지 않는 기법으로 불필요한 오버헤드를 최소화할 수 있다.
클라이언트와 서버에서 모두 이전 요청에 사용된 헤더 목록을 유지하게 되는데 요청에 대해서 중복된 헤더가 있을 경우(바뀌지 않은 부분)는 index 값만 전송하게 되고, 중복되지 않은 헤더(바뀐 부분)는 허프만 인코딩을 사용하여 HPACK 압축 방식으로 인코딩 처리하여 전송한다.
- 서버 푸시
HTTP/2에서는 클라이언트의 요청에 대해 미래에 필요할 것 같은 리소스를 미리 보낼 수 있다.
처음에는 이게 무슨 소리인가 싶었지만 잘 생각해 보면 가장 최근에 했던 프로젝트에서 프론트엔드로 Vue3를 사용한 적이 있었다.
Vue3 프로젝트에서는 index.html 파일에 내용이 담기게 되는데 웹 브라우저가 해당 index.html 파일을 요청하게 된다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="./homeicon2.ico">
<title>
SSM
</title>
<link rel="stylesheet" href="./vendor/css/fullcalendar.min.css" />
<link rel="stylesheet" href="./vendor/css/bootstrap.min.css">
<link rel="stylesheet" href='./vendor/css/select2.min.css' />
<link rel="stylesheet" href='./vendor/css/bootstrap-datetimepicker.min.css' />
<!---->
</head>
<body>
<div id="app">
</div>
<script src="./vendor/js/jquery.min.js"></script>
<script src="./vendor/js/bootstrap.min.js"></script>
<!---->
</body>
</html>
서버에서는 위의 코드를 보고 어떤 js 파일과 css 파일이 필요한지 알 수 있기 때문에 응답을 보내줄 때 미리 보내줌으로써 브라우저 캐시에 가져다 놓게 되고, 클라이언트(웹 브라우저)에서는 별도의 요청 없이도 리소스를 얻을 수 있다.
위의 예시를 살펴보면 GET 메서드로 index.html을 요청하면 서버에서는 해당 index.html 파일에 필요한 js 파일과 이미지, css 등을 바로 보내주게 된다.
앞서 살펴봤던 HTTP/2는 여러 가지 기능을 제공하면서 성능을 개선할 수 있지만 여전히 문제점을 가지고 있는데 그것은 바로 TCP 통신 기반이라는 것이다.
HTTP/2에서 제공하는 멀티 플렉싱, 서버 푸시 등을 이용하여 아무리 성능을 개선하였다고 해도 일단 HTTP는 기본적으로 TCP를 기반으로 통신하기 때문에 무조건 3-way handshake를 거쳐야 한다.
결국에는 연결을 맺는 과정이 절대적으로 필요하게 되면서 지연 시간이 발생할 수밖에 없는 구조인 것이다.
또한, 앞서 살펴봤던 멀티 플렉싱을 통해 HOL Blocking 문제를 해결할 수 있었지만 TCP 통신에서 패킷이 유실되거나 오류가 있을 때 재전송하는 과정에서 다시 HOL Blocking 문제가 발생할 수 있다.
문제점들을 살펴보면 결국에는 TCP가 문제인 것으로 요약할 수 있고, 이를 개선하기 위해 UDP 프로토콜을 사용하는 HTTP/3가 탄생하게 된다.
HTTP/3
HTTP/3는 QUIC이라는 계층 위에서 돌아가며 TCP가 아닌 UDP 기반으로 돌아간다.
구글링 해서 HTTP/3의 계층 구조를 찾아보면 신기하게도 QUIC이라는 계층이 전송 계층에 살짝 걸쳐있는 것을 볼 수 있다.
왜 그런가 찾아보니 응용 계층에 있는 HTTP/3는 QUIC을 동작시키기 위해 있는 것이고, QUIC은 UDP 기반으로 만들어졌기 때문에 전송 계층의 UDP 프로토콜 위에서 동작한다고 한다.
QUIC은 TCP를 사용하지 않기 때문에 통신을 시작할 때 번거로운 3-웨이 핸드셰이크 과정을 거치지 않아도 된다. 따라서 첫 연결 설정에 1-RTT만 소요되어 빠른 서비스 시간을 제공한다.
QUIC은 순방향 오류 수정 메커니즘이 적용되어 있어 전송한 패킷이 손실되었다면 수신 측에서 에러를 검출하고 수정하는 방식으로 열악한 네트워크 환경에서도 낮은 패킷 손실률을 자랑한다.
참고 자료
'CS > 네트워크' 카테고리의 다른 글
네트워크 - DNS (0) | 2024.07.13 |
---|---|
네트워크 - 대역폭 (0) | 2024.07.10 |
네트워크 - IP (0) | 2023.10.14 |
네트워크 - TCP와 UDP (0) | 2023.10.13 |
네트워크 - 네트워크 기기 (0) | 2023.10.13 |