HTTP
HyperText Transfer Protocol로, 하이퍼텍스트를 통해서 연결할 수 있는 HTML을 전송하는 프로토콜을 뜻한다. 현재는 모든 것을 HTTP 메시지에 담을 수 있다. HTML, TEXT, IMAGE, 음성, 영상, 파일, JSON, XML (API) 등 거의 모든 형태의 데이터를 전송 가능하다. 서버 간에 데이터를 주고 받을 때도 대부분 HTTP를 이용한다.
HTTP 역사
- [1991년] HTTP/0.9 : GET 메서드만 지원, HTTP 헤더 X
- [1996년] HTTP/1.0 : 메서드, 헤더 추가
- [1997년] HTTP/1.1 : 가장 많이 사용, 우리에게 가장 중요한 버전
- [1997년] RFC2068
- [1999년] RFC2616
- [2014년] RFC7230 ~ 7235
- [2015년] HTTP/2 : 성능 개선
- [진행 중] HTTP/3 : TCP 대신에 UDP 사용, 성능 개선
기반 프로토콜
- HTTP/1.1과 HTTP/2는 TCP 프로토콜 위에서 동작한다.
- HTTP/3은 UDP 프로토콜 위에서 동작한다.
- 왜 안정적인 TCP를 택하지 않았을까?
- TCP는 3 way handshake를 비롯하여 속도가 빠른 메커니즘이 아니다.
- HTTP/3는 UDP 프로토콜 위에서 성능을 최적화하도록 새로 설계해서 나왔다.
- 왜 안정적인 TCP를 택하지 않았을까?
- 현재 HTTP/1.1을 주로 사용 중이고 HTTP/2, HTTP/3도 점점 증가하고 있다.
- 네트워크 탭에서 네이버 접속 시 로드되는 리소스들로, h3(HTTP/3)와 h2(HTTP/2)를 확인할 수 있다.

HTTP 특징
- http는 클라이언트 서버 구조로 동작한다.
- 무상태 프로토콜(Stateless)을 지향한다.
- 비연결성이다.
- HTTP 메시지를 통해서 통신한다.
- 단순하고 확장 가능하다.
특징 1. 클라이언트 서버 구조
HTTP는 클라이언트가 HTTP 메시지를 통해서 서버에 요청을 보내면 클라이언트는 서버로부터 응답이 올 때까지 대기한다. 서버에서 요청에 대한 결과를 만들어서 클라이언트로 응답을 보내면 클라이언트가 그 응답 결과를 열어서 동작하게 된다.
클라이언트와 서버를 분리하는 것이 중요하다. (이전에는 이 개념이 분리되지 않았다.) 비지니스 로직이랑 데이터들을 서버가 처리하고, 클라이언트는 ui를 그리고 사용성에 집중하여 클라이언트와 서버가 각각 독립적으로 진화할 수 있다. 클라이언트는 복잡한 비즈니스 로직이 없고, 복잡한 데이터를 다룰 필요도 없어 단순하게 UI를 어떻게 그릴지에만 집중하면 된다. 서버는 서버의 아키텍처, 백엔드 기술을 어떻게 대용량 트래픽 기술로 고도화하고 진화할지만 집중하면 된다.
특징 2. 무상태 프로토콜 (Stateless) 지향
서버가 클라이언트의 상태를 보존하지 않는다는 의미이다.
- stateful과 stateless
- stateful: 서버가 클라이언트의 상태를 유지한다.
- 이전 요청의 맥락을 기억하고, 이후 요청에서 이를 기반으로 처리한다.
- 특정 서버에 종속된 상태 정보를 사용할 경우, 서버 변경 시 문제가 발생할 수 있다.
- 이 경우, 클라이언트는 필요한 상태 정보를 다시 전송하거나 초기화해야 한다.
- stateless: 서버가 클라이언트의 상태를 유지하지 않는다.
- 각 요청에 필요한 모든 정보를 포함하여 보내므로 서버 변경에 영향을 받지 않는다.
- 갑자기 클라이언트 요청이 증가해도 서버를 대거 투입할 수 있다.
- 응답 서버를 쉽게 바꿀 수 있어 무한한 서버를 증설할 수 있다.
- 오류가 생기거나 서버가 변경되어도 다른 서버에서 그대로 처리 가능하다.
- stateful: 서버가 클라이언트의 상태를 유지한다.
Stateless 설계에도 실무에서는 한계가 존재한다. 모든 기능을 완전히 무상태로 설계하는 것은 현실적으로 어려우며, 특히 로그인처럼 상태를 유지해야 하는 기능에서는 서버와 브라우저의 쿠키·세션을 조합하여 상태를 관리할 수밖에 없다. 이 경우 서버 세션이 사라지거나 서버가 다운되면 로그인 상태가 해제되는 한계가 있다. 또한 무상태는 각 요청에 모든 정보를 포함해야 하므로 데이터 전송량이 많아지는 단점도 있다. 따라서 웹 애플리케이션을 설계할 때는 가능한 한 stateless로 설계하되, 상태 유지는 정말 필요한 경우에만 최소한으로 사용하는 것이 바람직하다.
특징 3. 비연결성
TCP/IP는 기본적으로 연결을 유지하는 모델을 기반으로 한다. 예를 들어, 클라이언트 1이 서버와 TCP/IP 소켓을 연결한 후 요청을 보내고 응답을 받으면, 클라이언트 1과 서버 간의 연결이 유지된다. 이후 클라이언트 2가 서버에 접속하여 요청과 응답을 주고받더라도 클라이언트 1과의 연결은 계속 유지된다. 클라이언트 3 역시 동일하게 요청과 응답을 수행하는 동안, 클라이언트 1과 2의 연결은 계속 남아 있다. 이처럼 연결을 유지하는 모델에서는 서버가 여러 클라이언트와 동시에 연결을 유지해야 하므로 자원이 지속적으로 소모되는 단점이 있다. 즉, 클라이언트 1이 요청을 처리받는 동안에도 클라이언트 2와 3은 계속 연결 상태를 유지해야 한다는 것이다.
반면, 연결을 유지하지 않는 모델에서는 클라이언트와 서버가 TCP/IP 연결을 맺고 요청과 응답을 주고받은 뒤 즉시 연결을 종료한다. 클라이언트 2와 3도 마찬가지로 요청을 처리한 후 연결을 끊는다. 이렇게 하면 서버는 요청을 주고받는 순간에만 연결을 유지하게 되므로, 연결에 필요한 자원을 최소화할 수 있다. 이러한 방식을 비연결성이라고 한다.
HTTP는 기본적으로 연결을 유지하지 않는 비연결성 모델을 따른다. 일반적으로 초 단위 이하의 빠른 속도로 응답을 수행하기 때문에, 수천 명이 동시에 서비스를 이용하더라도 서버에서 처리하는 실제 요청 수는 크지 않다. 따라서 비연결성을 통해 서버 자원을 효율적으로 사용할 수 있다.
그러나 비연결성은 매번 TCP/IP 연결을 새로 맺어야 하므로 3-way handshake 시간이 추가되는 단점이 있다. 또한, 웹 브라우저가 사이트를 요청하면 HTML뿐 아니라 자바스크립트, CSS, 이미지 등 다양한 자원이 함께 다운로드되는데, 이러한 자원마다 연결하고 끊는 과정을 반복하면 비효율적이다. 이를 개선하기 위해 HTTP는 기본적으로 persistent connection(지속 연결)을 사용하며, HTTP/2와 HTTP/3에서는 이보다 더 많은 최적화가 적용되어 효율성을 높였다.
💡
Persistent connection, 즉 지속 연결은 HTTP에서 비연결성으로 인한 비효율성을 극복하기 위해 도입된 방식이다. 일반적인 비연결성 모델에서는 클라이언트가 요청을 보내고 서버가 응답을 반환하면 TCP/IP 연결을 종료하기 때문에, HTML, CSS, JavaScript, 이미지 등 여러 자원을 다운로드할 때마다 매번 연결을 새로 맺어야 한다. 이는 3-way handshake 과정이 반복되므로 성능에 부담이 된다. 반면, persistent connection을 사용하면 클라이언트와 서버 간의 TCP/IP 연결을 한 번 맺은 후, 여러 요청과 응답을 해당 연결에서 연속적으로 처리할 수 있다. 이로 인해 연결 설정과 해제에 소요되는 오버헤드를 줄일 수 있으며, 동시에 서버 자원의 효율적인 사용과 응답 속도 향상을 가능하게 한다. HTTP/1.1부터는 기본적으로 지속 연결을 지원하며, HTTP/2와 HTTP/3에서는 멀티플렉싱과 헤더 압축 등 추가 최적화를 통해 더욱 효율적인 연결 관리가 가능하다.
HTTP 메시지
- 구조
- start-line 시작 라인
- header 헤더
- empty line 공백라인 (CRLF)
- message body
-
- start-line 시작 라인 : GET /search?q=hello&hl=ko HTTP/1.1
- header 헤더 : Host: www.google.com
- empty line 공백라인 (CRLF)
- message body :요청 메시지
(+) 요청 메시지에도 body 본문을 가질 수 있다.GET /search?q=hello&hl=ko HTTP/1.1 Host: www.google.com
- 응답 메시지
start-line 시작 라인 : HTTP/1.1 200 OK header 헤더 : Content-Type: text/html;charset=UTF-8 Content-Length: 3423 empty line 공백라인 (CRLF) message body : ...
구조 1. 시작라인
`start-line = request-line / status-line`
시작라인은 request-line과 status-line으로 이루어져 있다.
`request-line = method SP(공백) request-target SP HTTP-versionCRLF (엔터)`
request-line은 요청 메시지로, 처음에 method에 http 메서드를 넣고 request-target에 path(요청 대상)을 넣으며, 마지막에 HTTP version이 들어간다.
HTTP 메서드의 종류로는 GET, POST, PUT, DELETE 등이 있고, 서버가 수행해야 할 동작을 지정한다. 요청대상은 보통 절대경로로 시작하여 쿼리들이 같이 들어가기도 한다. (absolute-path[?query]) 절대 경로는 “/”로 시작하는 경로를 뜻한다. http://…?x=y 와 같이 다른 유형의 경로 지정 방법도 존재한다.
`status-line = HTTP-version SP status code SP reason-phrase CRLF`
status-line은 응답 메시지이다. status code는 HTTP 상태 코드로, 요청의 성공 실패를 나타낸다. reason-phrase는 이유 문구로, 사람이 이해할 수 있는 짧은 상태 코드 설명 글이다.
HTTP 상태코드 - 200 (성공), 400 (클라이언트 요청 오류), 500 (서버 내부 오류)
구조 2. HTTP 헤더
`header-field = field-name: “:” OWS filed-value OWS`
- OWS는 띄어쓰기 허용을 의미한다.
- field-name은 대소문자 구문이 없다.
- ex. Host: www.google.com, Content-Type: text/html;charset=UTF-8, Content-Length: 3423
HTTP 헤더에는 HTTP 전송에 필요한 모든 부가정보가 들어가 있다. 메시지 바디의 내용, 메시지 바디의 크기, 압축, 인증, 요청 클라이언트(브라우저) 정보, 서버 애플리케이션 정보, 캐시 관리 정보 등을 예로 들 수 있다. 그리고 필요시 임의의 헤더를 추가할 수 있다.
구조 3. HTTP 메시지 바디
실제 전송할 데이터가 담겨 있다. HTML 문서, 이미지, 영상, JSON 등 byte로 표현될 수 있는 모든 데이터를 전송 가능하다.