웹의 애플리케이션 계층 프로토콜인 HTTP(HyperText Transfer Protocol)은 웹의 중심이라고 볼 수 있고, 이것은 RFC 1945, RFC 2616에 정의되어 있습니다. 서로 다른 종단 시스템(서버, 클라이언트)에서 수행되는 프로그램들은 서로 HTTP 메시지를 교환하여 통신합니다. HTTP는 메시지의 구조 및 클라이언트와 서버가 어떻게 교환하는지에 대해 정의하고 있습니다. HTTP를 자세히 설명하기 전에 몇 가지 웹 전문용어를 살펴보겠습니다.
웹 페이지(web page, 문서)는 객체들로 구성됩니다. 객체(object)는 단순히 단일 URL로 지정할 수 있는 하나의 파일(HTML, JPEG, GIF 등)입니다. 대부분의 웹 페이지는 기본 HTML 파일 과 여러 참조 객체로 구성됩니다. 예를 들어, 웹 페이지가 HTML 텍스트와 5개의 JPEG 이미지로 구성되어 있으면, 이 웹 페이지는 6개의 객체를 갖고 있는 것입니다(기본 HTML 파일과 5개의 이미지). 기본 HTML 파일은 페이지 내부의 다른 객체를 그 객체의 URL로 참조하니다. 각 URL은 2개의 요소, 즉 ‘객체를 갖고 있는 서버의 호스트 네임’과 ‘객체의 경로 이름’을 갖고 있습니다. 예를 들어,
http://sungjk.github.io/images/profile/scala.png
http://sungjk.github.io 은 호스트 네임이고, /images/profile/scala.png 는 경로 이름입니다. 웹 브라우저(크롬이나 파이어폭스 또는 IE)는 HTTP의 클라이언트 측을 구현하기 때문에 여기서는 단순히 웹의 관점에서 브라우저와 클라이언트라는 용어를 혼용하여 사용하겠습니다. HTTP의 서버 측을 구현하는 웹 서버(Web Server)는 URL로 각각을 지정할 수 있는 웹 객체를 갖고 있습니다.
HTTP는 웹 클라이언트가 웹 서버에게 웹 페이지를 어떻게 요청하는지와 서버가 클라이언트로 어떻게 웹 페이지를 전송하는지를 정의합니다. 이후에 클라이언트와 서버의 상호작용을 자세히 논하겠지만 일단 기본 개념은 다음과 같습니다. 사용자가 웹 페이지를 요청할 때, 브라우저는 페이지 내부의 객체에 대한 HTTP 요청 메시지를 서버에게 보냅니다. 서버는 요청을 수신하고 객체를 포함하는 HTTP 응답 메시지로 응답합니다.
HTTP는 TCP를 전송 프로토콜로 사용합니다. HTTP 클라이언트는 먼저 서버에 TCP 연결을 시작합니다. 일단 연결이 이루어지면, 브라우주와 서버 프로세스는 그들의 소켓 인터페이스를 통해 TCP로 접속합니다. 클라이언트 측에서 보면 소켓 인터페이스는 클라이언트 프로세스와 TCP 연결 사이에서의 출입구이고, 서버 측에서 보면 소켓 인터페이스는 서버 프로세스와 TCP 연결 사이에서의 출입구입니다. 클라이언트는 HTTP 요청 메시지를 소켓 인터페이스로 보내고 소켓 인터페이스로부터 HTTP 응답 메시지를 받습니다. 마찬가지로 HTTP 서버는 소켓 인터페이스로부터 요청 메시지를 받고 응답 메시지를 소켓 인터페이스로 보냅니다. 일단 클라이언트가 메시지를 소켓 인터페이스로 보내면, 메시지는 클라이언트의 손을 떠난 것이고, TCP의 손에 쥐어진 것입니다. TCP가 HTTP에게 신뢰적인 데이터 전송 서비스를 제공한다는 것은 다들 아시죠? 이것은 클라이언트 프로세스가 발생시킨 모든 HTTP 요청 메시지가 궁극적으로 서버에 잘 도착한다는 것을 의미합니다. 마찬가지로 서버 프로세스가 발생시킨 각 HTTP 응답 메시지로 클라이언트에 잘 도착한다는 것을 의미합니다. 여기서 계층구조의 중요한 장점 하나를 보게 됩니다. HTTP는 데이터의 손실 또는 TCP가 어떻게 손실 데이터를 복구하고 네트워크 내부에서 데이터를 올바른 순서로 배열하는지 걱정할 필요가 없습니다. 이것은 TCP와 프로토콜 스택의 하위 계층들이 하는 일입니다.
서버가 클라이언트에게 요청 파일을 보낼 때, 서버는 클라이언트에 관한 어떠한 상태 정보도 저장하지 않습니다. 만약 특정 클라이언트가 몇 초 후에 같은 객체를 두 번 요청한다면, 잠시 전에 이미 그 객체를 보냈다고 서버가 알려 주면 좋겠지만 서버는 이전에 한 일을 기억하지 않으므로 그 객체를 또 보냅니다. HTTP 서버는 클라이언트에 대한 정보를 유지하지 않으므로, HTTP를 비상태 프로토콜(stateless protocol)이라고 합니다.
Persistent & Non-persistent
클라이언트와 서버의 상호작용이 TCP 상에서 발생할 때 개발자는 결정을 할 필요가 있습니다. 각 요청/응답 쌍이 분리된 TCP 연결을 통해 보내져야 하는가? 혹은 모든 요청과 해당하는 응답들이 같은 TCP 연결상으로 보내져야 하는가? 전자 방식의 경우 애플리케이션은 비지속 연결(Non-persistent conneciton)이라고 하고, 후자 방식의 경우 지속 연결(Persistent connection)이라고 합니다.
Non-Persistent connection
웹 페이지를 서버에서 클라이언트로 전송하는 단계를 살펴보겠습니다. 페이지가 기본 HTML 파일과 10개의 JPEG 이미지로 구성되고, 이 11개의 객체가 같은 서버에 있다고 가정해보겠습니다. 기본 HTML 파일의 URL은 다음과 같습니다.
http://www.iana.org/domains/root/db/xn--9t4b11yi5a.html
연결 과정은 다음과 같습니다.
- HTTP 클라이언트는 HTTP의 기본 포트 번호 80(https는 443)을 통해 http://www.iana.org 서버로 TCP 연결을 시도한다. TCP 연결과 관련하여 클라이언트와 서버에 각각 소켓이 있게 된다.
- HTTP 클라이언트는 1단계에서 설정된 TCP 연결 소켓을 통해 서버로 HTTP 요청 메시지를 보낸다. 이 요청 메시지는 /domains/root/db/xn–9t4b11yi5a.html 경로 이름을 포함한다.
- HTTP 서버는 1단계에서 연결된 소켓을 통하여 요청 메시지를 받는다. 저장장치로부터 /domains/root/db/xn–9t4b11yi5a.html 객체를 추출한다. HTTP 응답 메시지에 그 객체를 캡슐화한다. 그리고 응답 메시지를 소켓을 통해 클라이언트로 보낸다.
- HTTP 서버는 TCP에게 TCP 연결을 끊으라고 한다(그러나 실제로 TCP 클라이언트가 응답 메시지를 올바로 받을 때까지 연결을 끊지 않는다).
- HTTP 클라이언트가 응답 메시지를 받으면, TCP 연결이 중단된다. 메시지는 캡슐화된 객체가 HTML 파일인 것을 나타낸다. 클라이언트가 응답 메시지로부터 파일을 추출하고 HTML 파일을 조사하고 10개의 JPEG 객체에 대한 참조를 찾는다.
- 그 이후에 참조되는 각 JPEG 객체에 대하여 처음 네 단계를 반복한다.
브라우저는 웹 페이지를 수신하면서, 사용자에게 그 페이지를 보여줍니다. 2개의 다른 브라우저는 웹 페이지를 서로 다른 방식으로 해석(렌더링)할 수 있습니다. HTTP는 클라이언트가 웹 페이지를 어떻게 해석하는지는 관심이 없습니다. HTTP 명세서(RFC 1945, RFC 2616)는 클라이언트 HTTP 프로그램과 서버 HTTP 프로그램 사이의 통신 프로토콜만 정의합니다.
앞 단계는 서버가 객체를 보낸 후에 각 TCP 연결이 끊어지므로 비지속 연결을 사용하고 있습니다(연결이 다른 객체를 위해 유지되지 않습니다). 각 TCP 연결은 하나의 요청 메시지와 하나의 응답 메시지만 전송합니다. 그래서 이 예에서는 사용자가 웹 페이지를 요청할 때 11개의 TCP 연결이 만들어집니다.
앞에 설명된 단계에서, 의도적으로 클라이언트가 10개의 순차적 TCP 연결을 통해 10개의 JPEG 파일을 얻는지 아니면 동시에 TCP 연결을 만들어 JPEG를 얻는지 언급하지 않았습니다. 사실 사용자가 동시성 정도를 조절할 수 있도록 브라우저를 구성할 수 있습니다(브라우저마다 동시에 연결 가능한 TCP 연결의 수가 다릅니다). 사용자가 원하면, 최대 동시 연결 수를 1로 할 수 있으며, 이 경우 10개의 연결이 순차 설정됩니다. 하지만 동시 연결을 하면 총 응답 시간을 줄일 수 있습니다.
Persistent connection
비지속 연결은 몇 가지 단점이 있습니다. 첫째, 각 요청 객체에 대한 새로운 연결이 설정되고 유지되어야 합니다. TCP 버퍼가 할당되어야 하고 TCP 변수들이 클라이언트와 서버 양쪽에 유지되어야 합니다. 이는 수많은 클라이언트의 요청을 동시에 서비스하는 웹 서버에게 부담을 줄 수도 있습니다. 둘째, 각 객체는 2 RTT(Round-Trip time)를 필요로 합니다(TCP 연결 설정에 1 RTT, 객체를 요청하고 받는데 1 RTT).
지속 연결에서 서버는 응답을 보낸 후에 TCP 연결을 그대로 유지합니다. 같은 클라이언트와 서버 간의 이후 요청과 응답은 같은 연결을 통해 보내집니다. 특히, 전체 웹 페이지(앞 예에서 HTML 파일과 10개의 이미지)를 하나의 지속 TCP 연결을 통해 보낼 수 있습니다. 또한 같은 서버에 있는 여러 웹 페이지들을 하나의 지속 TCP 연결을 통해 보낼 수 있습니다. 이들 객체에 대한 요구는 진행 중인 요구에 대한 응답을 기다리지 않고 연속해서 만들어 질 수 있습니다(파이프라이닝). 일반적으로 HTTP 서버는 일정 기간(타임아웃 기간) 사용되지 않으면 연결을 닫습니다. 서버가 연속된 요구를 수신할 때, 서버는 객체를 연속해서 보냅니다. HTTP의 디폴트 모드는 파이프라이닝을 이용한 지속 연결을 사용합니다.
HTTP 메시지 포맷
HTTP 명세서(RFC 1945, RFC 2616)는 요청과 응답에 관한 2 가지 HTTP 메시지 포맷을 정의합니다.
HTTP 요청 메시지
GET /hello.htm HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3)
Host: www.tutorialspoint.com
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: close
간단한 요청 메시지만 잘 분석하면 많은 것을 배울 수 있습니다. 첫째, 메시지가 일반 ASCII 텍스트로 쓰여 있어 사람들이 읽을 수 있습니다. 둘째, 메시지가 다섯 줄로 되어 있고 각 줄은 CR과 LF(carriage return & line feed)로 구별됩니다. 마지막 줄에 이어서 추가 CR과 LF가 따릅니다. 이 요청 메시지는 다섯 줄이지만, 더 많은 줄로 구성되거나 하나의 줄이 될 수도 있습니다. HTTP 요청 메시지의 첫 줄은 요청 라인(request line)이라고 부릅니다. 요청 라인은 3개의 필드(Method, URL, HTTP 버전)을 갖습니다. Method 필드는 GET, POST, HEAD, PUT 그리고 DELETE를 포함하는 여러가지 다른 값을 가질 수 있습니다. 예를 들어, GET 방식은 브라우저가 URL 필드로 식별되는 객체를 요청할 때 사용됩니다.
HTTP 응답 메시지
다음은 전형적인 HTTP 응답 메시지입니다.
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache/2.2.14 (Win32)
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
Content-Length: 88
Content-Type: text/html
Connection: close
여기에는 3개의 셋션, 즉 초기 상태 라인, 6개의 헤더 라인, 그리고 Body로 구성되어 있습니다. 상태 라인은 3개 필드, 즉 프로토콜 버전 필드
, 상태 필드
, 해당 상태 메시지
를 갖습니다. 이 예에서 서버는 HTTP/1.1을 사용하고 있고 모든 것이 양호함(OK)을 나타냅니다.
- Connection: close - 클라이언트에게 메시지를 보낸 후 TCP 연결을 닫는 데 사용한다.
- Date: - HTTP 응답이 서버에 의해 생성되고 보낸 날짜와 시간을 나타낸다. 이 시간이 객체가 생성되거나 마지막으로 수정된 시간을 의미하는 것이 아니라는 것에 유의하라. 서버가 객체를 응답 메시지에 삽입하여 응답 메시지를 보낸 시간을 의미한다.
- Server: - 메시지가 Apache 웹 서버에 의해 만들어졌음을 의미한다. 요청 메시지의 User-agent 헤더 라인과 비슷하다.
- Last-Modified: - 객체가 생성되거나 마지막으로 수정된 시간과 날짜를 나타낸다. 이 헤더는 객체를 로컬 클라이언트와 프록시 서버 같은 곳의 캐싱에 사용된다.
- Content-Length: - 송신되는 객체의 바이트 수를 나타낸다.
- Content-Type: - 개체 몸체 내부의 객체가 HTML 텍스트인 것을 나타낸다. 객체 타입은 파일 확장자로 나타내는 것이 아니라 공식적으로 Content-Type: 헤더로 나타낸다.
마지막으로 HTTP 상태 코드 몇 가지만 알아보겠습니다.
- 200 OK: 요청이 성공되었고, 정보가 응답으로 보내졌다.
- 301 Moved Permanently: 요청 객체가 영원히 이동되었다. 새로운 URL은 응답 메시지의 Location: 헤더에 나와 있다. 클라이언트 소프트웨어는 자동으로 새로운 URL을 추출한다.
- 400 Bad Request: 서버가 요청을 이해할 수 없다는 일반 오류 코드다.
- 404 Not Found: 요청한 해당 리소스가 서버에 존재하지 않는다.
- 505 HTTP Version Not Supported: 요청 HTTP 프로토콜 버전을 서버가 지원하지 않는다.