
이 글은 스터디를 진행하며 '모든 개발자를 위한 HTTP 웹 기본 지식 (김영한)' 강의를 듣고 정리한 글입니다.
1. 모든 것이 HTTP
HTTP란?
HTTP는 hypertext transfer protocol의 약자이다. HTML 등 링크를 통해 연결할 수 있는 문서(hypertext)를 전송하는 프로토콜로 시작하였다. 하지만 지금은 모든 형태의 데이터를 HTTP 프로토콜에 담아서 전송을 한다. HTML 텍스트 뿐만 아니라 이미지, 음성, 영상, 파일 그리고 서버끼리 통신할 때 사용하는 JSON, XML 등 거의 모든 형태의 데이터를 다 전송할 수 있다.
HTTP 역사
HTTP/0.9 1991년: GET 메서드만 지원, HTTP 헤더X
HTTP/1.0 1996년: 메서드, 헤더 추가
HTTP/1.1 1997년: 가장 많이 사용, 우리에게 가장 중요한 버전
- RFC2068 (1997) -> RFC2616 (1999) -> RFC7230~7235 (2014)
HTTP/2 2015년: 성능 개선
HTTP/3 진행중: TCP 대신에 UDP 사용, 성능 개선
HTTP/1.1 버전에 우리가 사용하는 거의 모든 HTTP 기능이 들어있으며, 이후에 나온 버전들은 거의 개선에 초점을 두었다.
HTTP가 기반하는 프로토콜은 버전마다 다른데, HTTP/1.1, HTTP/2는 TCP 기반으로 작동하며, HTTP/3 부터는 속도를 개선하기 위해 UDP 기반으로 작동된다. 다만 아직까지 주로 HTTP/1.1를 사용하고 있다.

각 HTTP가 어떤 버전으로 사용되고 있는지는 브라우저의 개발자 도구(F12)의 네트워크 탭에서 어떤 버전을 쓰는지 확인할 수 있다. Protocol 탭이 안보인다면 오른쪽 마우스 클릭 후 Protocol을 체크해주면 볼 수 있다. h2는 http2, h3는 htt3이다. 현재는 http2, http3의 사용도 많이 확산된 추세다.
HTTP에는 크게 아래와 같은 특징이 있다.
HTTP 특징
- 클라이언트 서버 구조
- 무상태 프로토콜(Stateless), 비연결성
- HTTP 메시지
- 단순함, 확장 가능
이 특징들에 대해서는 아래에서 자세하게 정리한다.
클라이언트 서버 구조

클라이언트는 서버에 요청을 보내고 응답을 대기한다. 이에 서버가 요청에 대한 결과로 응답 데이터를 클라이언트에게 다시 보낸다. 어찌보면 당연하고 단순하다고 볼 수 있는데, 클라이언트와 서버 개념을 분리한다는 것이 중요한 것이다. 예를들어 비지니스가 잘 되어서 트래픽이 100배 넘게 폭주했다고 가정하자. 만약 클라이언트 서버 구조로 이루어져있지 않다면 트래픽 처리에 대해서 양쪽 다 의존하게 되고 부담을 갖게 될 것이다. 하지만 HTTP의 클라이언트 서버 구조 덕분에 비지니스 로직과 데이터 로직은 서버에서, 클라이언트는 UI에만 집중할 수 있게 되었다.
무상태 프로토콜 (Stateless)
HTTP의 또 다른 중요한 특징 중에 하나는 무상태 프로토콜을 지향한다는 것이다. 영어로 Stateless라고 하는데, 서버가 클라이언트의 상태를 보존하지 않는다는 뜻이다. 무상태의 장점은 서버가 상태를 저장하거나 의존하지 않기 때문에 확장성이 높아진다는 것이다. 반면에, 단점은 클라이언트의 상태를 매번 알려줘야 하기 때문에 추가 데이터를 전송해야 한다는 것이다.
Stateless의 예시를 Stateful이라는 반대 개념부터 예시와 함께 알아보자.


Stateful은 상태가 유지된다. 고객 -> 점원에게 요청을 보냈을 때 고객의 요청들에 대한 상태가 유지되어 있기 때문에 점원은 유지된 상태를 확인하고 결제를 이어갈 수 있다. 하지만, 점원이 중간에 바뀐다면 바뀐 점원은 고객의 이전 상태를 알 수 없기 때문에 결제를 이어갈 수 없게 된다.

앞 예시를 그대로 클라이언트-서버 구조에 대입해볼 수 있다. 클라이언트의 요청이 특정 서버와 연결되어 요청-응답 상태가 유지되고 있다가 중간에 해당 서버가 장애가 생기면, 클라이언트는 처음부터 다시 요청을 진행해야하는 것이다.
이제 반대 개념인 무상태(Stateless)에 대해 예시와 함께 알아보자.


Stateless는 고객->점원 간의 요청 상태가 유지되지 않기 때문에 고객은 처음부터 매번 구체적인 요청을 점원에게 보낸다. 이러면 점원이 중간에 바뀌더라도 고객의 이전 요청 상태에 대해 알 필요 없이 원할한 결제를 이어나갈 수 있게된다.


앞 예시를 마찬가지로 클라이언트-서버 구조에 대입해보면 위 그림과 같다. 중간에 요청-응답을 하던 서버가 장애가 나도 구체적인 요청을 담고 있기 때문에 다른 서버와 요청-응답을 원활하게 이어나갈 수 있다. 이는 Scale Out, 수평 확장에 유리하여 예를 들어 사이트에 큰 이벤트가 있어 많은 트래픽이 몰렸을때 백엔드에서는 장비를 수십, 수백 대로 확 늘려버릴 수가 있게 되는 것이다.
하지만 이런 무상태(Stateless)에도 실무에서 한계가 존재한다. 이는 모든 것을 무상태로 설계 할 수 있는 경우도 있고 없는 경우도 있기 때문인데, 예를 들면 로그인이 필요 없는 단순한 서비스 소개 화면의 경우 무상태로 설계가 가능하지만 로그인과 같은 '상태'를 유지해야 하는 경우에는 Stateless 를 막무가내로 사용할 수 없다. 때문에 서버에서는 일반적으로 브라우저 쿠키와 서버 세션등을 사용해서 상태 유지를 해야한다. 이런 예외 상황이 있다고 해도 결국에는 무상태가 주는 장점들 때문에 상태유지는 최소한으로만 사용해야 하고 꼭 필요한, 어쩔 수 없는 경우에만 사용해야한다.
또 한가지 단점이 있다면 클라이언트가 보내는 요청에 매번 구체적인 정보(상태 데이터)를 담고 있어야 하기 때문에 데이터를 너무 많이 넘긴다는 단점이 있기도 하다.
비 연결성(connectionless)


클라이언트 1,2,3과 하나의 서버와 통신을 한다고 가정해보자. 클라이언트 1,2,3은 각각 해당 서버와 통신하고 있어 TCP/IP 연결을 유지한다고 하면 서버는 연결된 클라이언트만큼 서버 자원이 소모된다. 반면 비연결성의 특성을 적용한다면 클라이언트1과 서버가 요청-응답하고 TCP/IP 연결을 끊고, 클라이언트 2,3도 마찬가지로 요청-응답 이후에 연결을 끊게 된다. 이로서 HTTP는 서버 연결을 유지 하지 않아도 되며, 최소한의 자원을 유지할 수 있게된다.
비연결성이라는 HTTP의 특징 덕분에 한번 서버에 요청을 하더라도, 그 이후에 연결을 유지하지 않는다. 따라서, 초 단위 이하의 빠른 속도로 응답할 수 있으며, 1시간 동안 수천명이 서비스를 사용해도 실제 서버에 동시 처리되는 요청은 수십개 이하로 매우 작다 (ex. 실제로 정확히 동시에 검색 버튼을 여러번 누르는 일은 매우 적다)

하지만, 비연결성에도 한계는 분명이 존재한다. TCP/IP 연결을 매번 새로 맺어야한다는 문제가 생긴다는 것인데, 이는 3 way handshake 시간이 추가되어 비효율적일 수 있다. 또한, 웹 브라우저로 사이트를 요청하면 HTML 뿐만 아니라 자바스크립트, css, 추가 이미지 등 수 많은 자원이 함께 다운로드 되기 때문에 매번 요청을 보내야 한다는 불편함이 생겨나게 된다. 이런 초기 HTTP의 문제를 개선하고자 HTTP/1.1 버전부터는 Keep-Alive 라는 기능을 이용하여 지속 연결(Persistent Connection) 방식을 도입하게 되었다.

연결 시간에 대해서는 내부메커니즘에따라 달라질 수 있다. 이런 지속 연결 기능을 통해 웬만한 html 페이지 하나 받을 때 까지는 지속연결을 유지하여 요청을 모두 받고난 후에 연결이 종료되어 속도개선이 이루어졌다. HTTP2와 3에서는 이런 것들을 더 빠르게 해결했는데, HTTP3는 UDP 프로토콜을 이용하여 이 연결 속도 자체도 줄여버려 더 큰 속도개선을 이루어냈다.
번외로 HTTP의 특징이기도 하지만, Stateless한 설계란 말은 개발자들에게도 참 중요한 주제다. 대용량 트래픽 처리, 특히 특정 시간에 증가하는 트래픽, 예를 들면 선착순과 같은 상황에서 어떻게든 머리를 쥐어짜서 Stateless한 설계로 대용량 트래픽이 올 때 서버를 늘려 대응할 수 있어야 한다.
2. HTTP 메시지

HTTP 메시지는 크게 요청 메시지와 응답 메시지로 나뉜다. HTTP 메시지는 특정 구조로 이루어져있는데 다음 이미지들과 함께 알아보자.
시작 라인
시작 라인은 크게 reqeust-line, status-line으로 구분된다.

요청 메시지의 경우 request-line을 갖게 되는데, 이때 method SP(공백) request-target SP HTTP-version CRLF(엔터)의 구조를 갖는다. 요청 메시지에 들어갈 수 있는 HTTP 메서드는 GET, POST, PUT, DELETE등 여러가지가 있으며, 요청 대상은 절대 경로와 쿼리를 조합해서 작성할 수 있다. 또한 요청 메시지 시작 라인에는 HTTP 버전 정보가 함께 들어간다.

응답 메시지는 status-line으로 응답이 온다. status-line 구조는 HTTP-version SP status-code SP reason-phrase CRLF로 이루어져있다. 응답 메시지의 시작 라인에는 HTTP 상태 코드가 오는데, 요청 성공 혹은 실패를 나타낸다. 200은 성공, 400은 클라이언트 요청 오류, 500은 서버 내부 오류임을 알리는 코드다. 이유 문구 자리에는 OK가 있는데, 이는 사람이 보고 간단하게 확인할 수 있는 짧은 상태 코드 설명 문구이다.
헤더

헤더 필드는 필드 네임뒤에 ":"이 들어가고, 다음에 OWS 라는게 있는데 띄어쓰기를 허용한다는 의미다. 단 Host : 와 같이 필드 네임 띄고 : 는 안된다. 필드 네임은 대소문자 구분이 없으며(Host, host) 단, 뒤의 value는 대소문자를 구분한다. (www.google.com)
헤더의 용도는 HTTP 전송에 필요한 모든 부가정보를 담는다. 예를들면 메시지 바다의 내용, 메시지 바디의 크기, 압축, 인증, 요청 클라이언트(브라우저) 정보, 서버 애플리케이션 정보, 캐시 관리 정보 ..등이 해당된다. 요약하자면 Body 내용 빼고 필요한 모든 메타 데이터들이 담기는 곳으로 이해하면 된다.
적용할 수 있는 표준 헤더 종료는 굉장히 많고, 임의 헤더를 추가해서 보낼 수도 있다. 단, 약속한 클라이언트-서버만 이해할 수 있을 것이다.
메시지 바디
헤더에서 한칸 띄우고(반드시) 메시지 바디가 들어간다. 여기에는 실제 전송할 데이터가 들어있다. html 문서, 이미지, 영상, json 등 바이트로 표현할 수 있는 모든 데이터가 전송이 된다. 그리고 압축해서 보내면 압축된 해당 내용이 여기 메시지 바디에 담기게 된다.
지금까지 HTTP에 대해서 보면 상당히 단순한 기술이다. 머리, 가슴, 배 마냥 단순하게 이루어져있다.
복잡하고 잘 만들어져서 HTTP가 성공했다기 보다, 항상 크게 성공하는 기술은 단순하지만 확장 가능한 기술이 대부분 크게 성공하는 것 같다.
'CS' 카테고리의 다른 글
| URI와 웹 브라우저 요청 흐름 (4) | 2024.04.04 |
|---|---|
| 인터넷 네트워크 (0) | 2024.04.04 |
| 브라우저의 주소 표시줄에 url을 입력하면 어떤 일이 벌어질까? (2) | 2024.04.04 |
| Call by reference란 무엇이고 보통 어떻게 쓰이나요? (0) | 2024.02.01 |
| [CS] 트랜잭션과 무결성에 대하여 (2) | 2024.01.09 |