들어가며...

HTTP Web 서버를 구현해보면서 HTTP 메시지에 대해 정리를 해야겠다는 생각을 하게 되었다. HTTP 메시지는 단순한, 데이터의 구조화된 블록이다. HTTP 메시지는 다음과 같이 두 가지가 존재한다.

  • 클라이언트로부터의 요청
  • 서버로부터의 응답

HTTP_완벽_가이드_3-3.png

메시지는 기본적으로 위와 같이 시작줄(Start-Line), 헤더 블록(Header), 본문(Body) 세 부분으로 이루어진다.

  • 시작줄: 이것이 어떤 메시지인지 서술한다.
  • 헤더 블록: 속성
  • 본문: 데이터를 담고 있으며, 아예 없을 수도 있다.

보통 시작줄과 헤더 블록을 묶어서 헤드(head)라고 부른다.

메시지 문법

모든 HTTP 메시지는 요청 메시지와 응답 메시지 두 가지로 분류된다.

  • 요청 메시지: 웹 서버에 어떤 동작을 요구한다.
  • 응답 메시지: 요청의 결과를 클라이언트에게 돌려준다.

요청과 응답 모두 기본적으로 구조가 같다.

HTTP_완벽_가이드_3-4.png

위 그림은 GIF image를 가져오기 위한 요청과 응답 메시지를 보여준다.

요청 메시지 형식

요청 메시지를 간단히 나타내면 다음과 같다.

<메서드> <요청 URL> <버전>
<헤더>

<엔티티 본문>

이를 좀 더 자세히 보면 아래와 같은 형식이다.(RFC2616 기준)

Request       = Request-Line
                *(( general-header
                    | request-header
                    | entity-header ) CRLF)
                CRLF
                [ message-body ]

여기서 CRLF는 캐리지 리턴(ASCII 13, \r)과 개행 문자(ASCII 10, \n)로 구성된 두 글자의 줄바꿈 문자열이다.

  • 캐리지 리턴(CR, Carriage Return): 커서의 위치를 맨 앞으로 이동한다.
  • 라인 피드(LF, Line Feed): 커서를 한 칸 아래로 이동한다.(새로운 행 추가)

윈도우 OS는 개행문자로 CRLF를 사용하고, 유닉스 계열(리눅스, MAC)은 LF를 사용한다. HTTP 명세에 따른다면 줄바꿈 문자열은 CRLF를 개행문자로 사용하지만 견고한 애플리케이션이라면 LF 역시 받아들일 수 있어야 한다고 한다.

Request-Line은 위에서 말한 시작줄을 말한다. 이는 위에서 간단히 살펴봤듯이 아래와 같은 구성요소로 되어 있다.

Request-Line   = Method SP Request-URI SP HTTP-Version CRLF

Request-Line은 각각 SP(ASCII 32, )로 나누어져 있으며, 마지막은 CRLF로 끝난다.

응답 메시지 형식

응답 메시지 형식은 요청 메시지 형식에서 시작줄만 다르다.

<버전> <상태 코드> <사유 구절>
<헤더>

<엔티티 본문>

이를 좀 더 자세히 표현하면 아래와 같다.

Response      = Status-Line
                *(( general-header
                    | response-header
                    | entity-header ) CRLF)
                CRLF
                [ message-body ]

Status-Line은 응답 메시지의 시작줄을 말하며, 아래와 같이 구성되어 있다.

Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF

지금까지 요청, 응답 메시지 형식에 대해 알아보았다. 그 다음은 메시지로 구성되어 있는 요소들에 대해서 하나씩 살펴보겠다.

메서드(Method)

메서드는 서버에게 어떤 동작이 일어나야 하는지를 지정해주는 것이다. 각 메서드를 간단히 표로 나타내면 아래와 같다.

메서드 설명 본문이 있는가?
GET 서버에서 어떤 문서를 가져온다. 없음
HEAD 서버에서 어떤 문서에 대해 헤더만 가져온다. 없음
POST 서버가 처리해야 할 데이터를 보낸다. 있음
PUT 서버에 요청 메시지 본문을 저장한다. 있음
TRACE 메시지가 프락시를 거쳐 서버에 도달하는 과정을 추적한다. 없음
OPTIONS 서버가 어떤 메서드를 수행할 수 있는지 확인한다. 없음
DELETE 서버에서 문서를 제거한다. 없음

모든 서버가 위 표의 메서드를 모두 구현하는 것은 아니다. 더 나아가 서버마다 자기만의 메서드가 있을 수도 있다. 이러한 메서드는 확장 메서드라고 한다. 각 메서드를 좀 더 자세히 살펴보자.

GET

GET은 가장 흔히 쓰이는 메서드로 주로 서버에게 리소스를 달라고 요청할 때 쓰인다.

HTTP_완벽_가이드_3-7.png

HEAD 메서드는 정확히 GET과 같이 행동하지만, 서버는 응답으로 헤더만을 돌려준다. 엔티티 본문은 절대 반환되지 않는다. 이를 사용하는 이유는 일반적으로 다음과 같다.

  • 리소스를 가져오지 않고도 그에 대해 무엇인가(타입이라던가)를 알아낼 수 있다.
  • 응답의 상태 코드를 통해, 개체가 존재하는지 확인할 수 있다.
  • 헤더를 확인하여 리소스가 변경되었는지 검사할 수 있다.

서버 개발자는 반드시 반환되는 헤더가 GET으로 얻는 것과 정확히 일치함을 보장해야 한다. 또한 HTTP/1.1 준수를 위해서는 HEAD 메서드가 반드시 구현되어야 한다.

HTTP_완벽_가이드_3-8.png

PUT

PUT 메서드는 서버에 문서를 쓰는 역할을 한다. PUT 메서드로 요청받은 서버는 요청 URL의 이름대로 새 문서를 만들거나, 이미 존재한다면 요청한 본문으로 교체한다.

HTTP_완벽_가이드_3-9.png

POST

POST 메서드는 서버에 입력 데이터를 전송하기 위해 설계되었다. PUT 메서드와 비슷하게 생각할 수 있지만 PUT 메서드는 서버에 있는 리소스에 데이터를 입력하기 위함이다. POST와 PUT이 요청하는 데이터가 같을 수는 있지만 동작하는 의도가 다르다.

POST 메서드는 실제로 HTML의 form을 지원하기 위해 흔히 사용된다.

HTTP_완벽_가이드_3-10.png

TRACE

TRACE 메서드는 클라이언트에게 자신의 요청이 서버에 도달했을 때 어떻게 보이게 되는지 알려준다.

OPTIONS

OPTIONS 메서드는 웹 서버에게 여러 가지 종류의 지원 범위에 대해 물어본다. 아래의 그림과 같이 서버에게 특정 리소스에 대해 어떤 메서드가 지원되는지 물어볼 수 있다.

HTTP_완벽_가이드_3-12.png

DELETE

DELETE 메서드는 서버에게 요청 URL로 지정한 리소스를 삭제할 것을 요청한다. 그러나 클라이언트는 삭제가 수행되는 것을 보장받지 못한다. 왜냐하면 HTTP 명세는 서버가 클라이언트에게 알리지 않고 요청을 무시할 수 있기 때문이다.

HTTP 버전

HTTP 버전은 HTTP/x.y 형식으로 요청과 응답 메시지 모두 표시된다. 이는 프로토콜 버전을 상대방에게 말하기 위한 수단이다. 버전 번호는 해당 애플리케이션이 지원하는 가장 높은 HTTP 버전이다. 예를 들어 응답의 프로토콜 버전이 HTTP/1.1이라는 것은 응답을 보낸 애플리케이션이 이해할 수 있는 최대는 HTTP/1.1 버전이라는 것이다.

상태 코드(Status Code)와 사유 구절(Reason-Phrase)

응답줄은 숫자로 된 상태 코드와 문자열로 되어 있는 사유 구절을 통해 서버에서 요청을 처리한 결과로 무엇이 일어났는지 클라이언트에게 말해준다. 사유 구절은 문자열로 되어 있어 사람이 이해하기 쉽고 상태 코드는 프로그램을 통해 에러 처리를 쉽게 하기 위함이다.

전체 범위 정의된 범위 분류
100-199 100-101 정보
200-299 200-206 성공
300-399 300-305 리다이렉션
400-499 400-415 클라이언트 에러
500-599 500-505 서버 에러

현재 버전의 HTTP는 각 상태 분류에 대해 적은 수의 코드만을 정의했다. 이는 프로토콜이 진화하면서, 더 많은 상태 코드가 공식적으로 정의될 것으로 예상하기 때문이다. 그리고 누군가 정의된 범위 이 외에 자신만의 상태코드를 확장으로 정의할 수도 있다. 예를 들어 상태 코드 515를 받게 되면, 그 응답은 다른 5XX 메시지들과 같은 서버 에러로 간주하고 다루어야 한다. 각 범위에 대해 자주 사용하는 상태 코드들을 살펴보자.

100-199: 정보성 상태 코드

상태 코드 사유 구절 의미
100 Continue 요청의 시작부분 일부가 받아들여졌으며, 클라이언트는 나머지를 계속 이어서 보내야 함을 의미한다. 이것을 보낸 후 서버는 반드시 요청받아 응답해야 한다.
101 Switching Protocols 클라이언트가 Upgrade 헤더에 나열한 것 중 하나로 서버가 프로토콜을 바꿨음을 의미한다.

다음 상태 코드부터는 차근차근 정리할 예정

헤더

HTTP 헤더 필드는 요청과 응답 메시지에 추가 정보를 더한다. 이는 기본적으로 이름: 값 쌍의 목록이다. 헤더는 다음과 같이 분류된다.

  • 일반 헤더: 요청과 응답 양쪽 모두에 나타날 수 있다.
  • 요청 헤더: 요청에 대한 부가 정보를 제공한다.
  • 응답 헤더: 응답에 대한 부가 정보를 제공한다.
  • Entity 헤더: 본문 크기와 콘텐츠, 혹은 리소스 그 자체를 서술한다.
  • 확장 헤더: 명세에 정의되지 않은 새로운 헤더이다.

헤더는 여러 줄로 나누어 쓸 수 있다. 추가 줄 앞에 최소 하나의 스페이스 혹은 탭 문자가 와야 한다.

HTTP/1.0 200 OK
Content-Type: image/gif
Content_Length: 8572
Server: Test Server
  Version 1.0

위의 ServerTest Server Version 1.0 이라는 정보를 가지고 있는 것과 같다.

일반 헤더

헤더 설명
Connection 현재 트랜잭션이 끝난 후에 네트워크 연결을 열린 상태로 둘지 여부를 제어한다.
Date 메시지가 언제 만들어졌는지에 대한 날짜와 시간을 제공한다.
MIME-Version 발송자가 사용한 MIME의 버전을 알려준다.
Trailer chunked transfer 인코딩으로 인코딩된 메시지의 끝 부분에 위치한 헤더들의 목록을 나열한다.
Transfer-Encoding 수신자에게 안전한 전송을 위해 메시지에 어떤 인코딩이 적용되었는지 말해준다.
Upgrade 발송자가 '업그레이드'하길 원하는 새 버전이나 프로토콜을 알려준다.
Via 이 메시지가 어떤 중개자(프록시, 게이트웨이)를 거쳐 왔는지 보여준다.
  • 일반 캐시 헤더
헤더 설명
Cache-Control 메시지와 함께 캐싱 매커니즘을 전달하기 위해 사용한다.
Pragma 메시지와 함께 지시자를 전달하는 또 다른 방법. 캐시에 국한되지 않는다. (이 헤더는 Cache-Control로 인해 더이상 사용하지 않을 예정)

요청 헤더

요청 헤더는 요청 메시지에서만 의미를 갖는다.

헤더 설명
Client-IP 클라이언트가 실행된 컴퓨터의 IP를 제공한다.
From 클라이언트 사용자의 메일 주소를 제공한다.(RFC 822 이메일 주소 포맷)
Host 요청의 대상이 되는 서버의 호스트 명과 포트를 준다.
Referer 현재 페이지로 연결되는 링크가 있던 이전 웹 페이지의 주소이다.(바로 직전에 있었던 URL)
User-Agent 요청을 보낸 애플리케이션의 이름을 서버에게 제공한다.
  • Accept 관련 헤더

클라이언트는 서버에게 Accept 관련 헤더를 가지고 자신의 선호와 능력을 알려준다. 서버는 클라이언트로 부터 받은 Accept 헤더를 참고하여 클라이언트가 처리할 수 있는 데이터를 보내는 것이 좋다. 이 헤더는 서버와 클라이언트 사이의 네트워크 비용을 낭비하지 않도록 도와준다.

헤더 설명
Accept 서버에게 서버가 보내도 되는 미디어 종류(MIME 타입)를 말해준다.
Accept-Charset 서버에게 서버가 보내도 되는 문자집합을 말해준다.
Accept-Encoding 서버에게 서버가 보내도 되는 인코딩을 말해준다.
Accept-Language 서버에게 서버가 보내도 되는 언어를 말해준다.
TE 서버에게 서버가 보내도 되는 확장 전송 코딩을 말해준다.
  • 조건부 요청 헤더

클라이언트는 요청 메시지에 제약을 둘 수 있다. 예를 들어, 클라이언트가 이미 어떤 문서를 가지고 있다면 서버에게 이 문서가 자신이 가지고 있는 것과 달라진 경우에만 응답해달라고 할 수 있다.

헤더 설명
Expect 클라이언트가 요청에 필요한 서버의 행동을 열거할 수 있게 해준다.
If-Match 문서의 엔티티 태그(Etag)가 주어진 엔티티 태그와 일치하는 경우에만 문서를 가져온다.
If-Modified-Since 주어진 날짜 이후에 리소스가 변경된 경우에만 요청한다.
If-None-Match 문서의 Etag가 주어진 Etag와 일치하지 않는 경우에만 문서를 가져온다.
If-Range 문서의 특정 범위에 대한 요청을 할 수 있게 해준다.
If-Unmodified-Since 주어진 날짜 이후에 리소스가 변경되지 않은 경우에 요청한다.
Range 서버가 범위 요청을 지원한다면, 리소스에 대한 특정 범위를 요청한다.
  • 요청 보안 헤더

HTTP는 자체적으로 요청을 위한 간단한 인증요구/응답 체계를 가지고 있다. 이는 요청하는 클라이언트가 리소스에 접근하기 전에 어느정도 자신을 입증함으로써 트랜잭션을 좀 더 안전하게 하기 위함이다.

헤더 요청
Authorization 클라이언트가 서버에게 제공하는 인증 그 자체에 대한 정보를 담고 있다.
Cookie 클라이언트가 서버에게 토큰을 전달할 때 사용한다. 진짜 보안 헤더는 아니지만 보안에 영향을 줄 수 있다.
Cookie2 요청자가 지원하는 쿠키의 버전을 알려줄 때 사용한다.

응답 헤더

응답 헤더는 클라이언트에게 부가 정보를 제공한다. 누가 응답을 보내고, 응답자의 능력, 응답에 대한 특별한 설명 등을 포함하고 있다. 이 헤더들은 클라이언트에게 응답을 좀 더 잘 다루고 그 다음 요청에 도움이 되도록 하기 위함이다.

헤더 설명
Age 응답이 얼마나 오래되었는지 알려준다.(응답이 중개자를 통해 왔음을 암시)
Public 서버가 특정 리소스에 대해 지원하는 요청 메서드의 목록(최신에는 정의되지 않음)
Retry-After 현재 리소스가 사용 불가능한 상태일 때, 언제 가능해지는지에 대한 날짜 또는 시각
Server 서버 애플리케이션의 이름과 버전
Title HTML 문서에서 주어진 것과 같은 제목(RFC 2616에는 정의되지 않음)
Warning 사유 구절에 있는 것보다 더 자세한 경고 메시지
  • 협상 헤더

예를 들어 서버에 여러가지 언어를 제공한다면, 클라이언트가 어떤 언어를 선택할지 대한 협상을 할 수 있도록 지원한다.

헤더 설명
Accept-Ranges 서버가 자원에 대해 받아들일 수 있는 범위의 형태
Vary 서버가 확인해 보아야 하고 그렇기 때문에 응답에 영향을 줄 수 있는 헤더들의 목록
  • 응답 보안 헤더
헤더 설명
Proxy-Authenticate 프락시에서 클라이언트로 보낸 인증요구의 목록
Set-Cookie 서버가 클라이언트를 인증할 수 있도록 클라이언트 측에 토큰을 설정하기 위해 사용한다.(쿠키 정보 전송)
Set-Cookie2 Set-Cookie와 비슷하게 RFC 2965로 정의된 쿠키
WWW-Authenticate 서버에서 클라이언트로 보낸 인증요구의 목록

Entity 헤더

엔티티 헤더는 요청과 응답 모두에 엔티티를 포함할 수 있으므로, 양쪽 헤더에 모두 포함될 수 있다. 엔티티 헤더는 엔티티와 그 내용, 개체 타입, 주어진 리소스에 대해 요청할 수 있는 유효한 메서드 등 광범위한 정보를 제공한다. 일반적으로 엔티티 헤더는 메시지의 수신자에게 자신이 다루고 있는 것이 무엇인지 알려준다.

헤더 설명
Allow 이 엔티티에 대해 수행될 수 있는 요청 메서드들을 나열한다.
Location 클라이언트에게 엔티티가 실제로 어디에 위치하는지 알려준다. 일반적으로 수신자에게 리소스에 대한 위치(URL)을 알려줄 때 사용한다.
  • 콘텐츠 헤더

콘텐츠 헤더는 엔티티의 콘텐츠에 대한 자세한 정보를 제공한다. 예를 들어, 웹 브라우저는 Content-Type 헤더를 보고 그 객체를 어떻게 보여줄지 결정한다.

헤더 설명
Content-Base 본문에서 사용된 상대 URL을 계산하기 위한 기저 URL(RFC 2616에 정의되지 않음)
Content-Encoding 본문에 적용된 인코딩 정보
Content-Language 본문을 이해하는데 가장 적절한 자연어
Content-Length 본문의 길이나 크기
Content-Location 리소스가 실제로 어디에 위치하는지 정보
Content-MD5 본문의 MD5 체크섬(checksum)
Content-Range 전체 리소스에서 이 엔티티가 해당하는 범위를 바이트 단위로 표현
Content-Type 이 본문이 어떤 종류의 객치인지에 대한 정보
  • 엔티티 캐싱 헤더

일반 캐싱 헤더는 언제 어떻게 캐시가 되어야 하는지에 대한 지시자를 제공한다. 엔티티 캐싱 헤더는 엔티티에 대한 캐싱 정보를 제공한다. 예를 들어 리소스에 대한 캐시된 사본이 아직 유효한지, 캐시된 리소스가 유효하지 않게 되는 시점 등을 알려준다.

헤더 설명
ETag 이 엔티티에 대한 엔티티 태그, 기본적으로 리소스의 특정 버전에 대한 식별자이다.
Expires 이 엔티티가 더 이상 유효하지 않아 원본을 다시 받아와야 하는 일시
Last-Modified 가장 최근 이 엔티티가 변경된 일시

엔티티 본문

HTTP 메시지의 세 번째 부분은 선택적으로 사용되는 엔티티 본문이다. 여기에는 여러 가지 형식의 데이터를 보낼 수 있다. 예를 들어 이미지, HTML 문서, 소프트웨어 애플리케이션, 트랜잭션 등이 있다.

출처