HTTP?
HTTP?
HTTP는 현대 인터넷의 공용어다. 웹 브라우저, 서버 웹 어플은 모두 HTTP를 통해 서로 대화한다. 따라서, 웹을 이해하기 위해서는 HTTP를 잘 알아야 할 필요가 있다.
URL과 리소스
우리는 누구의 집이 어디에 있다는 것을 정확하게 표현하기 위해 '도로명 주소'나 '지번 주소'를 사용할 수 있다. 주소는 작명 표준이기 때문에 누구나 같은 주소 형식을 사용하고, 어떤 주소든 해석하고 위치를 찾을 수 있다.
마찬가지로 인터넷에서는 어떤 리소스의 주소를 표현하기 위해 'URL'(Uniform Resource Locator)를 사용한다. URL은 브라우저가 정보를 찾는데 필요한 리소스의 위치를 가리킨다. 그리고 URL을 통해 사람들은 HTTP 및 다른 프로토콜을 통해 리소스에 접근할 수 있다.
URL의 문법은 일반적으로 9개 부분으로 나눈다. 각 부분을 '컴포넌트'라고 부른다.
1<스킴>://<사용자이름>:<비밀번호>@<호스트>:<포트>/<경로>;<파라미터>?<질의>#<프래그먼트>
- 스킴: 사용할 프로토콜이다.
- 호스트와 포트: 리소스를 호스팅하고 있는 장비와, 그 장비 내에서 리소스에 접근할 수 있는 서버가 어디 있는지 나타낸다.
- 사용자 이름과 비밀번호: 대표적으로 FTP 서버의 경우 데이터에 접근하기 위해 사용자 이름과 비밀번호를 요구한다.
- 경로: 리소스가 서버의 어디에 있는지 알려준다.
- 파라미터: 서버에 정확한 요청을 하기 위해 필요한 입력 파라미터를 받는데 사용한다.
- 질의: 요청받을 리소스 형식의 범위를 좁히는데 사용한다.
&
로 나뉜이름=값
쌍 형식을 사용한다. - 프래그먼트: 클라이언트가 이를 사용해 리소스의 일부만 볼 수 있다. 서버에 전송하지 않는 컴포넌트다.
HTTP 메시지
HTTP가 인터넷의 배달원이라면, HTTP 메시지는 보낼 것을 담은 택배다.
HTTP 메시지는 HTTP 애플리케이션 간에 주고받은 데이터의 블록들이다. 데이터 블록들은 메시지의 내용과 의미를 설명하는 '텍스트 메타 정보'와 그 다음에 선택적으로 데이터가 올 수 있다.
HTTP 메시지는 시작줄, 헤더 블록, 본문의 세 부분으로 이루어진다.
1HTTP/1.0 200 OK2Content-type: text/plain3Content-length: 1945Hello, World!
시작줄은 어떤 메시지인지 알려주고, 헤더 블록은 속성을, 본문은 데이터를 담고 있다. 본문은 선택적이다.
시작줄과 헤더는 그냥 줄 단위로 분리된 아스키 문자열이다. 각 줄은 캐리지 리턴(CR, \r)과 개행 문자(LF, \n)로 구성된 두 글자의 줄바꿈 문자열로 끝난다. 이 줄바꿈 문자열을 합쳐 'CLRF'라고 부른다. 헤더의 목록은 빈 줄(CRLF)로 끝나 헤더 목록의 끝과 본문의 시작을 표시한다.
Carrage Return(CR)은 커서를 줄 올림 없이 앞으로 옮겨주고 Line Feed(LF)는 커서는 그대로 두고 종이를 한 줄 올리듯 줄바꿈 한다.
HTTP 문법
HTTP 메시지는 요청 메시지와 응답 메시지로 분류된다.
- 요청
1<메서드> <요청 URL> <버전>2<헤더>34<본문>
메서드는 서버에게 무엇을 해야하는지 말해준다. 예를 들어, GET 메서드는 서버에게 리소스를 달라고 요청하기 위해 쓰인다.
HTTP/1.1을 준수하기 위해서는 GET과 HEAD 메서드가 반드시 구현되어 있어야 한다.
HEAD는 GET처럼 행동하지만, 서버는 응답으로 헤더만을 돌려준다. 이때 헤더는 GET으로 얻은 것과 같아야 한다.
- 응답
1<버전> <상태 코드> <사유 구절>2<헤더>34<본문>
상태 코드는 클라이언트에게 무엇이 일어났는지 말해준다. 현재 버전의 HTTP에 정의된 상태 코드는 아래와 같다
전체 범위 | 정의된 범위 | 분류 |
---|---|---|
100-199 | 100-101 | 정보 |
200-299 | 200-206 | 성공 |
300-399 | 300-305 | 리다이렉션 |
400-499 | 400-415 | 클라이언트 에러 |
500-599 | 500-505 | 서버 에러 |
버전 번호는 자신이 지원하는 가장 높은 단계의 버전을 나타낸다.
즉, HTTP/1.1
로 응답을 받았다면 메시지가 HTTP/1.1
로 작성되었다는 뜻이 아니다.
버전 번호는 분수로 다뤄지지 않고, HTTP/x.y
의 x
와 y
는 각각 분리된 숫자로 다뤄진다.
HTTP Versions
HTTP/1.0 (Non-persistent HTTP)
HTTP/1.0에서는 리소스를 교환할 때마다 TCP 연결을 새로 열고 닫는다. 리소스를 요청할 때마다 TCP 연결에 1 RTT(Round Trip Time)와 요청에 1 RTT, 총 2 RTT가 소요된다. 여기에 요청한 리소스를 받는데 transmission time()까지 소요된다. 즉, HTTP 응답시간이 만큼 걸린다.
Keep-Alive
HTTP/1.0+에서 지속 커넥션을 맺기 위해 사용됐다. 설계상 문제를 가져 명세에는 빠졌지만, keep-alive handshake가 널리 사용되고 있기 때문에 Connection: Keep-Alive
헤더로 처리한다. 클라이언트가 keep-alive 헤더를 포함해서 보냈을때, 응답에 똑같은 헤더가 있다면 keep-alive를 지원한다는 의미다.
HTTP/1.1 (Persistent HTTP, Pipelining)
HTTP/1.1에서는 여러 리소스를 교환한 후 TCP 연결을 종료한다. 첫 요청을 제외하고는 이미 맺은 TCP 연결을 사용하기 때문에 평균적으로 1 RTT가 소요된다. 또한 파이프라이닝을 지원하기 때문에 첫번째 요청에 대한 응답이 완전히 전송되기 이전에 두번째 요청 전송이 가능하다. 가장 많이 사용하는 HTTP 버전이다.
FCFS(First-Come-First-Served)로 동작하기 때문에 작은 리소스가 큰 리소스 때문에 기다려야 하는 일이 발생한다. 이 문제를 HOL(Head-Of-Line) Blocking 이라고 한다.
HTTP/2.0 (Multiplexing)
HOL Blocking 문제를 완화하기 위해 리소스를 frame 단위로 나누어 보낸다.
이전까지는 HTTP 메시지가 ASCII를 사용했는데, HTTP/2.0 부터는 binary로 변경됐다. 또한, 유저가 요청할 것이라 생각되는 리소스를 요청 전에 미리 보내는 server push 기능도 추가됐다.
하지만 보안이 없고 loss recovery로 인한 지연이 발생하는 문제가 아직 존재한다.
HTTP/3.0
보안 기능을 추가했다. 그리고 TCP 대신 UDP를 사용해 per object error control과 congestion control을 지원한다.
HTTP 성능
HTTP의 성능은 TCP의 성능에 큰 영향을 받는다. HTTP의 지연원인은 다음과 같다.
- DNS resolve
요즘은 DNS cache로 인해 매우 빠르다. - TCP 연결(handshake)
TCP ACK 세그먼트는 HTTP 요청 메시지 전체를 전달할 수 있을 만큼 크다. 크기가 작은 트랜잭션은 TCP 연결에만 50% 이상의 시간을 소모하게 된다. - 확인응답지연
TCP는 데이터 패킷에 확인 응답을 편승(piggy backing)시킨다. TCP는 확인 응답을 편승시키기 위해 송출될 데이터를 일정기간 기다린다. 하지만 HTTP는 요청과 응답 두 가지로만 이뤄지기 때문에 편승할 기회가 많지 않다. 확인 응답이 오히려 지연을 발생시킬 수 있다. OS에 따라 관련 기능을 수정 및 비활성화 할 수 있다. - Nagle 알고리즘
TCP 세그먼트는 40바이트 상당의 flag와 헤더를 가진다. TCP가 작은 데이터를 가진 세그먼트를 많이 보내면 성능상 좋지 않기 때문에, 많은 세그먼트를 하나로 합쳐 보내는 방법이 nagle 알고리즘이다. 전송된 패킷에 대한 확인 응답을 받았거나 전송하기 충분한 양의 패킷이 쌓였을 때 전송한다. 이 방법에 몇가지 문제점이 있다. 크기가 작은 데이터는 추가적인 데이터를 기다리게 되고, 확인응답과 부정적인 시너지를 일으킨다. 확인응답은 편승할 패킷을 기다리는데, nagle 알고리즘은 패킷을 보내지 않고 대기하기 때문이다.TCP_NODELAY
소켓 옵션으로 비활성화 할 수 있다.
참조
- wikipedia
- MDN HTTP overview
- HTTP 완벽 가이드