HTTP

모든 것이 HTTP

  ·  5 min read

현대 시대는 모든 것을 HTTP 메시지에 담아 전송한다.1

HTML, TEXT, IMAGE, 음성, 영상, 파일, JSON, XML …

거의 모든 형태의 데이터 전송이 가능하며 서버간 데이터를 주고 받을 때도 대부분 HTTP를 사용한다.

HTTP 역사 #

  • HTTP/0.9 1991년: GET 메서드만 지원, HTTP 헤더 없음
  • HTTP/1.0 1996년: 메서드, 헤더 추가
  • HTTP/1.1 1997년: 가장 많이 사용되며 가장 중요하다
    • RFC2068(1997) $\rightarrow$ RFC2616(1999) $\rightarrow$ RFC7230~7235(2014)
    • 1.1 이후는 모두 성능 개선 위주의 변화
  • HTTP/2 2015년: 성능 개선
  • HTTP/3 진행중: TCP 대신 UDP 사용, 성능 개선

기반 프로토콜 #

  • TCP: HTTP/1.1, HTTP/2
  • UDP: HTTP/3

현재는 HTTP/1.1을 주로 사용하지만 2, 3버전도 점점 증가하고 있다.

HTTP 특징 #

HTTP는 아래의 4가지의 특징을 가지고 있다.

클라이언트 서버 구조 #

클라이언트와 서버를 분리함으로 비즈니스 로직과 데이터들은 서버에 넣고 클라이언트는 UI, UX 등과 같은 사용성에 집중한다. 이를 통해 클라이언트와 서버가 각각 독립적으로 진화될 수 있다. 사용자의 경험성을 높이기 위해서는 클라이언트를 집중적으로 향상시키면 되는 것이고 서버 트래픽을 더 잘 처리하기 위해서는 서버를 조금 더 고도화 하여 분리하여 개발할 수 있게 한다.

아주 간단하게 설명하자면 클라이언트는 서버에 요청을 보내고 응답을 대기한다. 서버는 요청에 대한 결과를 만들어 응답을 다시 클라이언트로 보내준다.

graph LR
    Client[Client]
    Server[Server]
    Database[Database]
    Client -->|Sends Request| Server
    Server -->|Fetches Data| Database
    Database -->|Returns Data| Server
    Server -->|Sends Response| Client

Stateless #

Stateless는 서버가 클라이언트의 상태를 보존하지 않는다는 것이다. 이와 달리 Stateful의 경우 클라이언트의 상태를 유지한다. Stateful의 경우 서버가 클라이언트를 기억하고 정보를 갖고 있으므로 클라이언트 입장에서는 편한 이용이 가능하다.

무상태, stateless의 경우 아래와 같은 대화를 예로 들 수 있다.

C: 이 노트북 얼마인가요?

S_1: 100만원입니다.

C: 노트북 2개 구매하겠습니다.

S_2: 노트북 2개는 200만원 입니다. 카드, 현금 중 어떻게 구매하시겠어요?

C: 노트북 2개를 카드로 구매하겠습니다.

S_3: 200만원 결제 완료되었습니다.

이처럼 모든 Request마다 서버(점원)가 바뀌어도 아무런 문제가 없이 요청을 하고 있다. 마지막 요청인 ‘노트북 2개를 카드로 구매하겠습니다.‘를 통해서 어떠한 클라이언트의 사전 정보 필요 없이도 클라이언트가 원하는 요청을 들어줄 수 있다. 그러므로 고객이 급속도로 증가해도 서버를 대거 투입하여 관리할 수 있다.(문맥이 필요 없으므로) 만약 Satetful 정책을 취한다면 클라이언트마다 서버를 서로 중계하여야 하며 해당 서버가 오류가 나면 문맥이 다 소실되어 처음부터 다시 시작해야 한다는 단점이 있다. sateless의 경우 중계 서버가 클라이언트에 따라 특정 서버를 중계하는 것이 아닌 무작위로 서버를 연결하여도 정상 작동하므로 확장성이 매우 증대된다.

Stateless의 한계

모든 것을 무상태로 설계할 수는 없다. 로그인 기능이 있따면 서버는 클라이언트가 로그인 상태가 유지되도록 해 주어야 한다. 그렇기 때문에 100% stateless로 할 수는 없다. 하지만 상태 정보의 경우 최소한으로만 유지해야 하며 될 수 있으면 stateless를 유지해야 한다.

비연결성 #

graph LR
    Client1[Client1] <-->|Request / Response| Server
    Client2[Client2] <-->|Request / Response| Server
    Client3[Client3] <-->|Request / Response| Server

위의 그래프는 모든 클라이언트들이 서버와 통신하지 않고 있어도 계속 연결을 유지하고 있다. 따라서 idle 상태에서도 서버 유지 하는 자원이 많아진다.

하지만 아래와 같이 비연결성을 유지한다면 자원을 아끼면서 빠른 속도로 통신할 수 있다. 아래의 그래프는 Client2만 연결을 하고 있기 때문에 연결이 유지되어 있지만 나머지 클라이언트는 연결을 유지하지 않고 있다.

graph LR
    Client1[Client1]
    Client2[Client2] <-->|Request / Response| Server
    Client3[Client3]

비연결성의 장점

  • 서버 자원의 효율적 사용
  • 빠른 속도 보장
  • HTTP 기본이 연결을 유지하지 않는 모델 채택

비연결성의 한계

  • TCP/IP 연결을 새로 맺어야 함
    • 3 way handshake 시간이 추가 된다.
      • 3 way handshake는 TCP(Transmission Control Protocol) 연결을 설정하기 위한 과정으로 클라이언트와 서버 간 신뢰할 수 있는 연결을 성립하기 위해 사용된다.
        sequenceDiagram
        participant Client
        participant Server
    
        Client->>Server: SYN (seq=x)
        Server->>Client: SYN-ACK (seq=y, ack=x+1)
        Client->>Server: ACK (seq=x+1, ack=y+1)
    
    • 웹 브라우저로 사이트를 요청한다면 Javascript, css, 추가 이미지 등 수많은 리소스가 동시에 다운로드 된다.
    • 현재는 HTTP 지속 연결(PErsistent Connections)로 문제 해결
    • HTTP/2, HTTP/3 에서 더 많은 최적화

Persistent Connections

sequenceDiagram
participant Client
participant Server
        
  Client->Server: Connection(0.1sec)
  Server->Client: Request/ HTML Response(0.1sec)
  Client->Server: Close(0.1sec)

  Client->Server: Connection(0.1sec)
  Server->Client: Request/ JavaScript Response(0.1sec)
  Client->Server: Close(0.1sec)

  Client->Server: Connection(0.1sec)
  Server->Client: Request/ Image Response(0.1sec)
  Client->Server: Close(0.1sec)

HTTP 초기에는 연결과 종료를 매우 낭비하고 있음을 알 수 있다. 대략 모든 연결을 끝마치면 0.9초가 걸림을 알 수 있다. 하지만 Persistent Connections를 사용한다면 아래와 같이 최적화를 할 수 있다.

sequenceDiagram
  participant Client
  participant Server

  Client->Server: Connection(0.1sec)
  Server->Client: Request/ HTML Response(0.1sec)
  Server->Client: Request/ JavaScript Response(0.1sec)
  Server->Client: Request/ Image Response(0.1sec)
  Client->Server: Close(0.1sec)

총 시간은 앞서 Persistent Connections를 쓰지 않을 때보다 거의 반절로 줄어든 것을 알 수 있다.

HTTP 메시지 #

HTTP 메시지 구조

+-------------------------+
|     HTTP Message        |
+-------------------------+
| Start Line              |
| (Method, URL, Version)  |
+-------------------------+
|                         |
| Headers                 |
| (Key-Value Pairs)       |
|                         |
+-------------------------+
| Blank Line (CRLF)       |
+-------------------------+
|                         |
| Body (Optional)         |
| (e.g., JSON, HTML)      |
|                         |
+-------------------------+

따라서 요청 메시지는 아래와 같이 전송된다.

GET /search?q=hello&hl=ko HTTP/1.1
Host: www.google.com
# Empty Line(CRLF)
# Body

서버가 Request를 받으면 응답은 아래와 같이 전송된다.

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 3423
# Empty Line(CRLF)
<html>
    <body> .. </body>
</html>

시작 라인

Request의 경우 start-line $\equiv$ request-line이다.

$$ \mathbf{request-line = (Method)\ SP\ (request-target)\ SP\ (HTTP-version)\ CRLF} \newline $$

$$ \mathbf{{GET,\ POST,\ PUT,\ DELETE} \subset Method} $$

  • request target
    • absolute-path[?query]의 형태를 가짐
    • /query?~~

Response의 경우 start-line $\equiv$ status-line이다.

$$ \mathbf{status-line = (HTTP-version)\ SP\ (status-code)\ SP\ (reason-phase)\ CRLF} \newline $$

Reason phase는 사람이 쉽게 이해할 수 있는 단문구를 말한다.

HTTP 헤더 #

  • header-field = field-name:‘OWS’field-value’OWS’
    • OWS = Optional White Space
  • field-name은 대소문자 구분이 없다.
  • 단, value는 대소문자 구분을 한다.

HTTP 헤더의 용도

헤더에는 HTTP 전송에 필요한 모든 부가정보가 들어가 있다. 메세지 바디의 내용, 크기, 압축, 인증, 요청 클라이언트(브라우저) 정보, 서버 애플리케이션 정보, 캐시 관리 정보 …

표준 헤더의 경우 종류가 아주 다양하며 필요하다면 임의의 헤더를 추가 가능하다.

HTTP 메세지 바디 #

메세지 바디는 실전송 데이터가 들어가있다. 바이트로 표현할 수 있는 모든 데이터를 전송할 수 있다.

정리 #

  • HTTP 메시지에 모든 것을 전송
  • HTTP/1.1을 기준으로 학습
  • 클라이언트-서버 구조
  • Stateless protocol
  • HTTP message
  • 단순함, 확장 가능
  • 현대는 HTTP의 시대

  1. “모든 개발자를 위한 HTTP 웹 기본 지식 강의 | 김영한 - 인프런,” 인프런. https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC/dashboard ↩︎