이 글은 책 ‘그림으로 배우는 HTTP & Network Basic’ 를 읽고 내용에 필자의 생각과 지식을 덧붙여 정리한 내용이다. 책 내용과 100% 일치하지 않을 수 있으며, 개인적인 견해나 경험이 들어갈 수 있다.
HTTP는 클라이언트와 서버 간에 통신을 한다
HTTP에서는 리소스를 요청하는 쪽이 클라이언트가 되고, 리소스를 제공하는 쪽이 서버가 된다. HTTP 통신에서는 어느 한쪽이 반드시 클라이언트, 다른 한쪽이 서버가 된다. HTTP는 클라이언트와 서버의 역할을 명확히 구분하고 있다.
리퀘스트와 리스폰스를 교환하여 성립
HTTP는 클라이언트로부터 리퀘스트가 송신되고, 그 결과가 서버로부터 리스폰스로 되돌아온다. 즉, 반드시 클라이언트로부터 통신이 시작된다.
GET /index.html HTTP/1.1
Host: www.hackr.jp
위는 클라이언트가 HTTP 서버에 송신한 리퀘스트 내용이다. GET
은 서버에 요구하는 종류를 나타내며 메소드라고 불린다. /index.html
은 요구 대상인 리소스를 나타내고 있으며 리퀘스트 URI라고 한다. HTTP/1.1
은 HTTP 버전 번호이다.
POST(메소드) /form/entry(URI) HTTP/1.1(프로토콜 버전)
Host: hackr.jp (리퀘스트 헤더 필드)
Connection: keep-alive (리퀘스트 헤더 필드)
Content-Type: application/x-www-form-urlencoded (리퀘스트 헤더 필드)
Content-Length: 16 (리퀘스트 헤더 필드)
name=ueno&age=37 (엔티티)
리퀘스트 메시지는 위와 같이 메소드
, URI
, 프로토콜 버전
, 옵션 리퀘스트 헤더 필드
와 엔티티
로 이뤄져있다.
이어서 리퀘스트를 받은 서버는 리퀘스트 처리 결과를 리스폰스로 클라이언트에게 아래와 같이 돌려준다.
HTTP/1.1 200 OK
Date: Tue, 10 Jul 2012 06:50:15 GMT
Content-Length: 362
Content-Type: text/html
<html>
...
HTTP/1.1
은 프로토콜 버전을, 200 OK
는 상태 코드와 상태 코드의 설명(프레이즈)이다. 2~4번째 줄은 리스폰스 헤더 필드이다. 그리고 한줄로 구분되어 응답 바디가 시작된다.
HTTP는 상태를 유지하지 않는 프로토콜
HTTP는 스테이트리스(stateless) 프로토콜이다. HTTP 혼자서는 리퀘스트와 리스폰스를 교환하는 동안 상태를 관리하지 않는다. HTTP 프로토콜은 이전에 보낸 리퀘스트나 이미 되돌려준 리스폰스에 대해 기억하고 있지 않다. 클라이언트, 서버 둘다 전에 뭘 보냈는지 기억하지 않는다는 뜻 이다.
이런 간단한 구조로 인해 HTTP는 확장성(scalability)을 확보할 수 있게 되었다. (책에서는 범위성이라고 번역되어 있었는데, 적절치 않은 것 같아 확장성으로 적었다) 또한 상태를 유지하지 않아 CPU, 메모리와 같은 리소스를 절약할 수 있고, 단순한 프로토콜이라는 특성으로 HTTP가 다양한 곳에서 사용될 수 있게 되었다.
하지만 웹이 발전하면서 스테이트리스 특징으로는 처리하기 어려운 일들이 많이 생겼다. 이를테면 로그인 상태 유지 등이 있다. 따라서 쿠키라는 기술이 도입된다.
리퀘스트 URI로 리소스를 식별
HTTP는 URI(Uniform Resource Indentifier)를 사용해서 인터넷 상의 리소스를 지정한다. 이 URI 덕분에 인터넷의 어떤 장소에 있는 리소스를 호출할 수 있게 되었다. 리퀘스트의 리퀘스트 URI를 지정하는 방법이 여러가지 있다.
모든 URI를 리퀘스트 URI에 포함한다.
GET http://hackr.jp/index.htm HTTP/1.1
Host 헤더 필드에 네트워크 로케이션을 포함한다.
GET /index.html HTTP/1.1
Host: hackr.jp
특정 자원이 아닌 서버를 대상으로 리퀘스트 송신
이런 경우 리퀘스트 URI에 *
를 지정할 수 있다. 아래는 서버가 지원하는 메소드를 묻는 예시이다.
OPTIONS * HTTP/1.1
서버에 임무를 부여하는 HTTP 메소드
붙임: 후술될 메소드들의 역할은 HTTP가 설계될 때 원래 부여된 목적이므로 REST와 혼동해서는 안된다.
GET: 리소스 획득
리소스를 획득할 때 사용되는 메소드이다. 리소스가 텍스트이면 그대로 반환하고, CGI와 같은 프로그램이면 동적으로 실행된 결과를 반환한다.
POST: 엔티티 전송
엔티티를 전송하기 위해 사용된다. GET으로도 엔티티를 전송할 수 있지만 일반적으로 사용되지 않는다. GET과 비슷해보이지만, POST는 리스폰스에 의한 엔티티를 획득하는 것만이 목적은 아니다.
PUT: 파일 전송
파일을 전송하기 위해 사용된다. FTP 프로토콜을 통한 업로드와 같이 리퀘스트에 포함된 엔티티를 URI로 지정한 곳에 저장하도록 요구한다. 단, HTTP/1.1 PUT에는 인증 기능이 없어 누구나 파일을 업로드할 수 있게 되는 보안 문제가 발생한다. 따라서 일반적인 웹 사이트에서는 사용되지 않는다.
REST와 같은 설계 양식에서 사용되는 경우가 있다.
HEAD: 메시지 헤더 취득
GET과 같은 기능이지만, 메시지 바디(엔티티)는 받아오지 않고 오직 헤더만 가져온다.
DELETE: 파일 삭제
파일을 삭제하기 위해 사용된다. PUT 메소드와 마찬가지로 보안 문제로 인해서 실제 웹사이트에서는 원래의 목적으로는 사용되지 않으며, REST와 같은 설계 양식에서 사용된다.
OPTIONS: 제공하고 있는 메소드 문의
리퀘스트 URI로 지정한 리소스가 제공하는 메소드의 종류를 조사할 때 사용한다.
TRACE: 경로 조사
웹 서버에 접속해서 자신에게 통신을 되돌려 받는 루프백(loop-back)을 발생시킨다. 리퀘스트를 보낼 때 Max-Forward라는 헤더 필드에 수치를 포함하고, 서버를 통과할 때 마다 그 수치를 줄여나간다. 수치가 0이 된 곳을 끝으로, 리퀘스트를 마지막응로 수신한 곳에서 상태 코드 200 OK 리스폰스를 되돌려준다. 이 메소드를 사용해서 리퀘스트를 보낸 곳에 어떤 리퀘스트가 가공되어있는지 조사할 수 있다. 이는 프록시 등을 중계하여 오리진 서버에 접속할 때 그 동작을 확인하기 위해 사용된다.
하지만 TRACE는 잘 사용되지 않고 XST와 같은 공격을 일으키는 보안 문제도 존재하여 사용되지 않는다.
CONNECT: 프록시에 터널링 요구
프록시에 터널 접속 확립을 요함으로써, TCP 통신을 터널링 시키기 위해서 사용된다. 주로 SSL이랑 TLS등의 프로토콜로 암호화된 것을 터널링 시키기 위해 사용되고 있다. CONNECT 메소드의 양식은 아래와 같이 이루어져 있다.
CONNECT 프록시 서버 : 포트 HTTP 버전
LINK: 리소스 간 링크 관계를 확립
리소스 간에 링크 관계를 확립할 때 사용된다. HTTP/1.1 버전에서 폐기되었다.
UNLINK: 링크 관계 삭제
링크 관계를 삭제할 때 사용된다. HTTP/1.1 버전에서 폐기되었다.
TRACE, CONNECT 메소드는 아직 이해가 잘 안된다. 조금 더 별도로 공부가 필요할 것 같다.
메소드를 사용해서 지시를 내리다
상술한 메소드는 리소스에 어떤 행동을 하기 원하는지 지시하기 위해 사용된다. 메소드는 대문자와 소문자를 구별하기 때문에 대문자로 기재할 필요가 있다.
지속 연결로 접속량을 절약
HTTP 초기 버전은 통신 한번에 TCP 커넥션이 발생하였다. 초기 통신에서는 작은 사이즈의 텍스트만을 주고 받아서 큰 문제 없었지만, 시간이 흐르며 다량의 이미지를 포함한 HTML 문서를 주고 받아야하는 경우가 늘었다. 이렇게 되면 HTML 문서에 포함된 여러장의 이미지를 획득할 때마다 HTTP 통신이 발생하고, TCP 연결/종료 과정이 발생하여, 최종적으로 통신량이 늘어난다.
지속 연결
HTTP/1.1과 일부 HTTP/1.0에서는 이런 문제를 해결하기 위해 지속 연결(Persistent Connections)라는 방법을 고안하였다. 지속 연결은 한 쪽이 명시적으로 연결을 종료하지 않는 이상 TCP 연결을 계속 유지한다. 즉, 1회의 TCP 커넥션으로 여러번의 HTTP 리퀘스트와 리스폰스가 이루어진다. 따라서 TCP 연결에서 발생하는 오버헤드를 줄여주고, 서버에 대한 부하가 경감되어 최종적으로 더 빠르게 웹페이지를 로드할 수 있다.
파이프라인화
지속 연결을 통해 HTTP 파이프라인(HTTP pipelining)이 가능하다. 이전에는 리퀘스트를 보내고 리스폰스를 기다린 다음, 리스폰스가 도착하면 다음 리퀘스트를 보냈다면 HTTP 파이프라인을 사용하면 리스폰스를 기다리지 않고 바로 다음 리퀘스트를 보낼 수 있게 되었다.
쿠키를 사용한 상태 관리
쿠키와 세션 (ft. HTTP의 비연결성과 비상태성) 포스팅에서 다루었으므로 생략한다.