0. 서론

이번 포스트에는 HTTP Protocol의 특성에 대해 알아보자.

1. TL;DR

  • HTTP는 클라이언트-서버 간 요청(Request)과 응답(Response)이 이루어지는 통신 프로토콜이다.
  • HTTP는 Stateless 프로토콜이다.
  • 클라이언트는 URI로 원하는 리소스를, Method로 원하는 종류의 Request를 보낼 수 있다.
  • HTTP는 매 통신마다 커넥션의 체결과 해제가 이루어진다. 그러나 최신 HTTP(<1.1)는 명시적인 요청이 이루어지지 않는 한 커넥션을 유지한다.
  • HTTP는 Stateless 서버의 단점을 보완하기 위해 쿠키(Cookie)를 이용한다.

2. HTTP를 이용한 통신

HTTP의 통신은 클라이언트(Client)와 서버(Server)의 상호작용을 의미한다. 매 통신마다 클라이언트와 서버의 역할은 고정되어 있지 않지만 모든 개개의 통신에는 클라이언트 역할의 기기와 서버 역할의 기기가 반드시 정해져 있다.


이 때 클라이언트는 서버에 리소스를 요청(Request)하는 개체이고, 서버는 그러한 요청이 왔을 때 요청자에게 리소스를 제공(Response)하는 개체이다.


따라서, 서버는 클라이언트의 Request를 수신하기 전까지는 Response를 생성하지 않는다.

3. Stateless Protocol

컴퓨터 네트워크의 프로토콜(혹은 서버)는 크게 두 가지로 분류할 수 있으며, 아래와 같다.

  • Stateful Server
  • Stateless Server

이 둘의 차이는 클라이언트와 서버가 서로 연결되어 리소스를 주고받는 동안 서버가 클라이언트의 상태(혹은 정보)를 관리하는지, 그렇지 않은지에 따라 다르다.


소켓 통신(Socket Server)으로 대표되는 Stateful Server는 클라이언트와 서버가 연결(Bind)된 후 커넥션이 유지되는 동안 서버가 클라이언트의 상태를 관리한다. 이 때 클라이언트의 상태는 클라이언트의 주소부터 시작해 클라이언트가 제공한 데이터 등 다양한 범주의 정보를 의미한다.


그러나 HTTP로 대표되는 Stateless Server는 클라이언트와 서버가 연결된 후 커넥션이 유지되는 동안에도 매 Request/Response가 완료될 때마다 서버는 클라이언트의 정보를 제거한다. 더 정확히는 통신 과정에서 클라이언트의 정보를 전혀 저장하지 않는다. 이는 보다 많은 클라이언트에 대해 서버가 적은 부하를 받을 수 있게 함으로써 많은 데이터를 매우 빠르고 확실하게 처리하는 범위성(Scalability)을 확보하기 위한 설계 전략이다.


그러나 이러한 방식은 웹 환경을 구축하는 데 한계로서 작용하기로 했는데, 대표적인 예가 로그인 정보 저장 등이다. 인강 사이트에 로그인 한 유저가 질문 게시판에 접속하려 한다고 가정해 보자. Stateless서버에서는 로그인 이력이나 정보를 전혀 저장하지 않기 때문에 새 페이지에 접근하는 매 순간마다 로그인을 요청할 것이다.


이러한 한계를 해결하기 위해 HTTP는 Stateful 서버를 차용하는 대신 쿠키(Cookie)라는 기술을 추가하였다.

4. 쿠키(Cookie)

앞서 말했듯이 HTTP는 Stateless 프로토콜이기 때문에 과거에 클라이언트와 교환했던 Request/Response 상태를 관리하지 않는다. 이러한 구조의 단점을 해결하기 위해 도입된 기술이 바로 쿠키(Cookie)라는 개념이다.


쿠키는 서버가 클라이언트의 Request에 대한 Response를 보낼 때 현재 Request-Response상태를 식별할 수 있게 해 주는 추가 정보를 의미한다. 서버는 쿠키를 Response에 추가한 후 Response Header에 Set-Cookie 필드를 설정함으로써 클라이언트가 보내준 쿠키를 저장하게 한다.

만약 클라이언트가 쿠키를 저장하게 되었다면 이후 동일한 서버에 Request를 할 때마다 쿠키를 함께 보내게 되는데, 서버는 이 때 전달받은 Request에 추가된 쿠키 정보를 바탕으로 이전의 요청/응답 상태를 알 수 있게 된다.


예를 들자면, 만약 어떤 쇼핑몰에 클라이언트가 서버에 로그인을 요청했고, 서버가 인증을 마친 뒤 로그인 완료 응답을 보냄과 동시에 이에 대한 쿠키 정보를 전송했다고 하자. 이 경우 클라이언트가 이후 해당 쇼핑몰의 한 상품 카탈로그에 접속하거나 단순히 새로고침을 하는 경우에 서버에 새 Request를 보내게 되는데, 이 때 로그인 정보(ID, 로그인 시간 등)가 담긴 쿠키를 함께 보내면 서버가 쿠키를 확인하고 요청을 보낸 클라이언트가 로그인 상태임을 확인할 수 있게 되는 것이다.


추가로, Stateless 서버임에도 불구하고 HTTP 서버는 각 클라이언트(정확히는 각 브라우저)마다 이들에 대한 정보를 담을 수 있는 약간의 메모리를 사용할 수 있는데, 이를 세션(Session)이라 한다. 세션에는 Request가 들어온 브라우저별로 최소한의 정보를 저장할 수 있어 통신 과정에서의 유연성을 높일 수 있게 하고, 클라이언트가 관리한다는 점으로 인해 보안성에 취약한 쿠키에 비해 높은 보안성을 지닌다.

5. URI와 Method

지금까지 HTTP 프로토콜을 이용해 클라이언트와 서버가 통신을 하는 구조와 한계, 해결 방안을 살펴보았다.

그렇다면 통신 과정에서 정확히 클라이언트와 서버는 어떤 형태의 데이터를 주고받을까?


클라이언트는 서버에게 리소스를 요청해야 하는데, 이 때 정확히 어떠한 리소스를 요청하는지 명시해야 한다. 이는 이전 포스트에서 살펴본 리소스 식별자인 URI를 서버에게 명시함으로써 가능하다.


그리고 웹 페이지를 이용해 본 사람들이라면 모두 알겠지만 클라이언트는 단순히 서버에 저장된 리소스를 제공받기만 하지 않는다. 때로는 서버에 자신의 리소스를 전달함으로써 저장을 요청하는 경우도 있고, 서버에 존재하는 리소스의 수정을 요청할 수도 있어야 한다.


이러한 다양한 요구 사항을 구현하기 위해 HTTP는 메소드(Method)라는 개념을 통해 클라이언트가 서버에 여러 형태의 요청을 할 수 있도록 되어 있다. 즉, 메소드는 Request의 종류라고 볼 수 있다. 어떤 종류의 요청을 하느냐에 따라 서버는 다르게 동작하고, 같은 데이터도 다르게 가공된 형태를 전달할 수 있다.


HTTP Method는 다음 종류가 존재한다.

(참고로, 아래에서 엔티티(Entity)라는 용어는 크기가 큰 파일 뿐만 아니라 매우 작은 신호들(변수나 플래그)까지 포함하는 개념이다)

Method 역할 서버로의 엔티티 전달 설명
GET 엔티티 획득 X 서버로부터 엔티티를 받아오기 위한 메소드. 가장 간단한 형태이다.
POST 엔티티 전송 O 서버에 엔티티를 제공하기 위한 메소드
PUT 리소스 전송 O 서버에 리소스를 제공하기 위한 메소드. POST와 달리 크기가 큰 파일 등을 전송한다
HEAD 헤더 취득 X GET과 동일한 역할을 하지만 패킷의 헤더만을 얻기 위해 사용한다.
DELETE 리소스 삭제 X 서버에 존재하는 리소스의 제거를 요청한다.
TRACE 경로 조사 X 서버에 루프백 메시지를 전달한다. 루프백 메시지는 자기 자신에게 돌아오는 메시지를 뜻한다. 이 때 메시지의 헤더에는 MAX-FORWARDS 필드가 존재하며 이 필드에 설정된 정수 만큼의 라우팅이 이루어지면 실제 서버까지 도달하지 않더라도 클라이언트로 메시지가 돌아오게 된다.
CONNECT 터널링 요구 X 프록시에 터널링을 요구한다. 보통 TCP 통신을 터널링 하기 위해 사용되며, SSL이나 TLS 프로토콜을 통해 암호화된 정보를 전달하기 위해 사용되기도 한다.

위 메소드들을 이용해 원하는 종류의 요청을 서버에 보낼 수 있게 된다.

6. 지속 연결(Persistent Connections)

앞서 HTTP는 Stateless서버라고 소개하였고, 따라서 별도의 통신 이력이나 클라이언트 정보를 저장하지 않는다. 더 나아가서 HTTP 1.0에서는 매 통신마다 TCP 연결과 해제가 이루어졌는데, 이는 서버 부하 자체를 최소화할 수 있고 작은 크기의 리소스를 보내는 데 최적화된 설계였다.


당시 웹 환경은 텍스트 파일 위주의 리소스 전송이 이루어졌기 때문인데, 현대의 웹 환경에서는 이와 같은 설계가 단점으로 작용하는 경우가 더 많다. 사진이나 동영상과 같은 큰 파일을 전송하기도 하고, 여러 번의 리퀘스트를 연속적으로 서버에 날리기도 한다. 이 경우 매 리퀘스트마다 TCP 체결과 해제를 반복하는 구조는 매우 느린 통신 속도를 유발하게 된다.

이러한 단점을 해소하기 위해 HTTP 1.1버전 이후로는 지속 연결(Persistent Connections)이라는 개념이 추가되었다. 이는 클라이언트나 서버 어느 한 쪽이 명시적으로 연결 종료를 선언하지 않는 이상 TCP 커넥션을 유지하는 기술이다.


지속 연결 사양이 추가된 서버와 클라이언트는 다음과 같은 형태로 통신하게 된다.

서버의 부하가 늘어나지만 TCP커넥션의 연결/종료로 반복되는 오버헤드(Overhead)를 감소시킬 수 있게 되었으며, 하드웨어의 발전 덕분에 서버의 부하 증가로 인해 발생하는 리스크도 많이 줄게 되었기 때문에 현재에는 거의 모든 서버와 클라이언트에 지속 연결 사양이 추가되어 있다.