HTTP 메시지, 메서드, 캐시

본 포스트는 O’REILLY 사의 HTTP 완벽가이드를 읽으면서 정리한 내용입니다.

HTTP 메시지는 HTTP 애플리케이션 간에 주고받은 데이터의 블록들이다. HTTP가 인터넷의 배달원이라면, HTTP 메시지는 무언가를 담아 보내는 소포라고 할 수 있다.

데이터의 블록들은 메시지의 내용과 의미를 설명하는 텍스트 메타 정보로 시작하고 그 다음에 선택적으로 데이터가 올 수 있다.

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

HTTP 메시지

https://www3.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html

요청 메시지와 응답 메시지 모두 기본적으로 구조가 같다. 시작줄 - 헤더 - 엔티티 본문으로 구성된다. 시작줄은 이것이 어떤 메시지인지 서술하며, 헤더 블록은 속성을, 본문은 데이터를 담고 있다. 본문은 아예 없을 수도 있다.

요청 메시지의 형식은 다음과 같다.

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

<헤더>

<엔티티 본문>

응답 메시지의 형식은 다음과 같다. (시작줄에서만 문법이 조금 다르다)

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

<헤더>

<엔티티 본문>

차근차근 각 형식을 이루는 개념이 무엇인지 뜯어보자.

메서드

클라이언트 측에서 서버가 리소스에 대해 수행해주길 바라는 동작이다.

GET

주로 서버에게 리소스를 달라고 요청하기 위해 쓰인다. HTTP/1.1은 서버가 이 메서드를 구현할 것을 요구한다.

GET 메서드

https://www3.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html

HEAD

GET과 작동 방식은 같으나, 서버는 응답으로 헤더만을 돌려준다. 엔티티 본문은 반환되지 않는다. 이를 사용하면 클라이언트가 리소스를 실제로 가져올 필요 없이 헤더만을 조사할 수 있도록 해준다. HEAD를 사용하면

  • 리소스를 가져오지 않고도 리소스의 타입 등 정보를 알아낼 수 있다.
  • 응답의 상태 코드를 통해 개체가 존재하는지 확인할 수 있다.
  • 헤더를 확인하여 리소스가 변경되었는지 검사할 수 있다.

PUT

PUT 메서드는 서버에 문서를 쓴다. PUT 메서드는 서버가 요청 본문을 가지고 요청 URL의 이름대로 새 문서를 만들거나, 이미 URL이 존재한다면 본문을 사용해서 교체하는 것을 의미한다.PUT은 콘텐츠를 변경할 수 있도록 하기 때문에 많은 웹 서버가 PUT을 수행하기 전에 사용자에게 비밀번호를 입력해서 로그인을 하도록 요구할 것이다.

POST

서버에 입력 데이터를 전송하기 위해 설계되었다.(POST는 서버에 데이터를 보내기 위해 사용되고, PUT은 서버에 있는 리소스(i.e.파일)에 데이터를 입력하기 위해 사용한다. HTML 폼을 지원하기 위해 흔히 사용된다. 채워진 폼에 담긴 데이터는 서버로 전송되며, 서버는 이를 모아서 필요로 하는 곳으로 보낸다.

TRACE

클라이언트가 어떤 요청을 할 때, 그 요청은 방화벽, 프락시, 게이트웨이 등의 애플리케이션을 통과할 수 있다. 이들이 원래의 HTTP 요청을 수정할 수 있기 때문에 TRACE 메서드는 클라이언트가 자신의 요청이 서버에 도달했을때 어떻게 보이게 되는지 알려준다.

TRACE 요청은 목적지 서버에서 루프백(loopback) 진단을 시작한다. 요청 전송의 마지막 단계에 있는 서버는 자신이 받은 요청 메시지를 본문에 넣어 TRACE 응답을 되돌려준다. 클라이언트는 자신과 목적지 서버 사이에 있는 모든 HTTP 애플리케이션의 요청/응답 연쇄를 따라가면서 자신이 보낸 메시지가 망가졌거나 수정되었는지, 만약 그렇다면 어떻게 변경되었는지 확인할 수 있다.

TRACE 메서드는 주로 진단을 위해 사용된다. 예를 들면 요청이 의도한 요청/응답 연쇄를 거쳐가는지 검사할 수 있다. 또한 프락시나 다른 애플리케이션들이 요청에 어떤 영향을 미치는지 확인하고자 할 때 좋은 도구다.

TRACE 요청은 어떠한 엔티티 본문도 보낼 수 없다. TRACE 응답의 엔티티 본문에는 서버가 받은 요청이 그대로 들어있다.

OPTIONS

OPTIONS 메서드는 웹 서버에게 여러가지 종류의 지원 범위에 대해 물어본다. 서버에게 특정 리소스에 대해 어떤 메서드가 지원되는지 물어볼 수 있다. OPTIONS 요청이 모든 리소스에 대한 것이므로 서버는 자신의 리소스에 대해 지원하는 메서드의 목록을 반환한다.

Example :-

200 OK

Allow: HEAD,GET,PUT,DELETE,OPTIONS

DELETE

서버에게 요청 URL로 지정한 리소스를 삭제할 것을 요청한다. 그러나 이러한 요청이 삭제가 수행되는 것을 보장하는 것은 아니다. HTTP 명세는 서버가 클라이언트에게 알리지 않고 요청을 무시하는 것을 허용하기 때문이다.

확장 메서드

확장메서드는 HTTP/1.1 명세에 정의되지 않은 메서드다. HTTP는 필요에 따라 확장해도 문제가 없도록 설계되어 있으므로 새로 기능을 추가해도 과거에 구현된 소프트웨어들의 오동작을 유발하지 않는다. 프락시는 종단 간(end-to-end) 행위를 망가뜨리지 않을 수 있담녀 알려지지 않은 메서드가 담긴 메시지를 다운스트림 서버로 전달하려고 시도한다. 그렇지 않다면 프락시는 501 Not Implemented 상태 코드로 응답해야 한다. 확장 메서드를 다룰 때는 “엄격하게 보내고 관대하게 받아들여라”라는 오랜 규칙에 따르는 것이 좋다.

  • LOCK

    사용자가 리소스를 잠글 수 있게 해준다. 예를 들어, 문서를 편집하는 동안 다른 사람이 동시에 같은 문서를 편집하지 못하도록 문서를 잠글 수 있다.

  • COPY

    서버에 있는 리소스를 복사한다.

  • MOVE

    서버에 있는 리소스를 옮긴다.

상태 코드

HTTP 상태 코드는 크게 다섯 가지로 나뉜다.

100-199 정보성 상태 코드

정보성 상태 코드는 HTTP/1.1에서 도입되었다. 이는 비교적 새로운 것이며 복잡함을 감수할 만한 가치가 있는가에 대해 논란이 되고 있다.

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

100 Continue 는 HTTP 클라이언트 애플리케이션이 서버에 엔티티 본문을 전송하기 전에 그 엔티티 본문을 서버가 받아들일 것인지 확인하려고 할 때, 그 확인 작업을 최적화하기 위한 의도로 도입된 것이다.

클라이언트와 100 Continue

만약 클라이언트가 엔티티를 서버에게 보내려고 하고, 그 전에 100 Continue 응답을 기다리겠다면 클라이언트는 값을 100-continue로 하는 Expect 요청 헤더를 보낼 필요가 있다. 100-continue는 여러 측면에서 최적화를 위한 것이다. 클라이언트 애플리케이션은 100-continue를 서버가 다루거나 사용할 수 없는 큰 엔티티를 서버에게 보내지 않으려는 목적으로만 사용해야 한다. 100-continue 값이 담긴 Expect 헤더를 보낸 이후에 클라이언트는 서버가 100-Continue 응답을 보내주기를 막연히 기다리기만 해서는 안된다. 약간의 타임아웃 후에 클라이언트는 그냥 엔티티를 보내야 한다.

서버와 100 Continue

서버가 100-continue 값이 담긴 Expect 헤더가 포함된 요청을 받는다면, 100 Continue 응답 혹은 에러 코드로 답해야 한다. 서버는 절대로 100-continue 응답을 받을 것을 의도하지 않은 클라이언트에게 100 Continue 상태코드를 보내서는 안된다. 서버가 100 Continue 응답을 보낼 기회를 갖기 전에 어떤 이유로 인해 엔티티의 일부 혹은 전체를 수신하였ㄷ면 서버는 이 상태 코드를 보낼 필요가 없다. 그러나 서버가 요청을 끝까지 다 읽은 후에는 그 요청에 대한 최종 응답을 보내야 한다.(100 Continue 상태는 생략 가능)

프락시와 100 Continue

프락시는 클라이언트로부터 100-continue 응답을 의도한 요청을 받는다. 만약 다음 홉(next-hop) 서버가 HTTP/1.1을 따르거나 혹은 어떤 버전을 따르는지 모른다면 Expect 헤더를 포함시켜서 요청을 다음으로 전달해야 한다. 만약 다음 홉의 서버가 1.1보다 이전 버전의 HTTP를 따른다는 것을 알고 있다면 프락시는 417 Expectation Failed 에러로 응답해야 한다.

200-299 성공 상태 코드

상태코드   사유구절 의미
200 OK 요청은 정상이고 엔티티 본문은 요청된 리소스를 포함하고 있다.
201 Created 서버 개체를 생성하라는 요청(i.e. PUT)을 위한 것. 응답은 생성된 리소스에 대한 최대한 구체적인 참조가 담긴 Location 헤더와 함께 그 리소스를 참조가능한 여러 URL을 엔티티 본문에 포함해야 한다.
202 Accepted 요청은 받아들여졌으나 서버는 아직 그에 대한 어떤 동작도 수행하지 않았다. 서버가 요청의 처리를 완료할 것인지에 대한 어떤 보장도 없다. 단지 요청이 받아들이기에 적법해 보인다는 의미일 뿐이다. 서버는 엔티티 본문에 요청에 대한 상태와 가급적이면 요청의 처리가 언제 완료될 것인지에 대한 추정(혹은 그에 대한 정보를 어디서 얻을 수 있는지)도 포함해야 한다.
203 Non-Authoritative Information 엔티티 헤더에 들어있는 정보가 원래 서버가 아닌 리소스의 사본에서 왔다. 중개자가 리소스의 사본을 갖고 있었지만 리소스에 대한 메타정보(헤더)를 검증하지 못한(혹은 안 한) 경우 이런 일이 발생할 수 있다.
204 No Content 응답 메시지는 헤더와 상태줄을 포함하지만 엔티티 본문은 포함하지 않는다. 주로 웹 브라우저를 새 문서로 이동시키지 않고 갱신하고자 할 때 (i.e. 폼을 리프레시) 사용한다.
205 Reset Content 브라우저에게 현재 페이지에 있는 HTML 폼에 채워진 모든 값을 비우라고 말한다.
206 Partial Content 부분 혹은 범위 요청이 성공했다. 206 응답은 Content-Range와 Date 헤더를 반드시 포함해야 하며, Etag와 Content-Location 중 하나의 헤더도 반드시 포함해야 한다.

300-399 리다이렉션 상태 코드

리다이렉션 상태 코드는 클라이언트가 관심있어 하는 리소스에 대해 다른 위치를 사용하라고 말해주거나 그 리소스의 내용 대신 다른 대안 응답을 제공한다. 만약 리소스가 옮겨졌다면 클라이언트에게 리소스가 옮겨졌으며 어디서 찾을 수 있는지 알려주기 위해 리다이렉션 상태 코드와 (선택적으로) Location 헤더를 보내줄 수 있다. 이는 브라우저가 사용자를 귀찮게 하지 않고 알아서 새 위치로 이동할 수 있게 해준다.

일반적으로 HEAD가 아닌 요청에 대해 리다이렉션 상태 코드를 포함한 응답을 할 때, 리다이렉트될 URL에 대한 링크와 설명을 포함시키는 것은 좋은 습관이다.

상태코드   사유구절 의미
300 Multiple Choices 클라이언트가 동시에 여러 리소스를 가리키는 URL을 요청한 경우, 그 리소스 목록과 함께 반환한다. 사용자는 목록에서 원하는 하나를 선택할 수 있다. (관련 내용 클라이언트 협상 참조) 서버는 Location 헤더에 선호하는 URL을 포함할 수 있다.
301 Moved Permanently 요청한 URL이 옮겨졌을 때 사용한다. 응답은 Location 헤더에 현재 리소스가 존재하고 있는 URL을 포함해야 한다.
302 Found 301 상태코드와 같다. 그러나 클라이언트는 Location 헤더로 주어진 URL을 리소스를 임시로 가리키기 위한 목적으로 사용해야 한다. 이후의 요청에서는 원래 URL을 사용해야 한다.
303 See Other 클라이언트에게 리소스를 다른 URL에서 가져와야 한다고 말해주고자 할 때 쓰인다. 새 URL은 응답 메시지의 Location 헤더에 들어있다. 이 상태 코드의 주 목적은 POST 요청에 대한 응답으로 클라이언트에게 리소스의 위치를 알려주는 것이다.
304 Not Modified 클라이언트는 헤더를 이용해 조건부 요청을 만들 수 있다. 만약 클라이언트가 GET과 같은 조건부 요청을 보냈고 그 리소스가 최근에 수정된 일이 없다면 이 코드는 리소스가 수정되지 않았음을 가리킨다. 이 상태 코드를 동반한 응답은 엔티티 본문을 가져서는 안된다.
305 Use Proxy 리소스가 반드시 프락시를 통해서 접근되어야 함을 나타내기 위해 사용한다.
307 Temporary Redirect 301 상태코드와 비슷하다. 그러나 클라이언트는 Location 헤더로 주어진 URL을 리소스를 임시로 가리키기 위한 목적으로 사용해야 한다. 이후의 요청에서는 원래 URL을 사용해야 한다.

400-499 클라이언트 에러 상태 코드

상태코드   사유구절 의미
400 Bad Request 클라이언트가 잘못된 요청을 보냈다고 말해준다.
401 Unauthorized 리소스를 얻기 전에 클라이언트에게 스스로를 인증하라고 요구하는 내용의 응답을 반환한다.
403 Forbidden 요청이 서버에 의해 거부되었음을 알려주기 위해 사용한다. 만약 서버가 거부의 이유를 알려주고자 한다면 그 이유를 엔티티 본문에 포함할 수 있다. 그러나 이 코드는 보통 서버가 거절의 이유를 숨기고 싶을 때 사용한다.
404 Not Found 서버가 요청한 URL을 찾을 수 없음을 나타내기 위해 사용한다.
405 Method Not Allowed 요청한 URL에 대해 지원하지 않는 메서드로 요청받았을 때 사용한다.
406 Not Acceptable 클라이언트는 자신이 어떤 종류의 엔티티를 받아들이고자 하는 지에 대해 매개변수로 명시할 수 있다. 이 코드는 주어진 URL에 대한 리소스 중에 클라이언트가 받아들일 수 있는 것이 없는 경우 사용한다. 종종 서버는 클라이언트에게 왜 요청이 만족될 수 없었는지 알려주는 헤더를 포함시킨다.
407 Proxy Authentication Required 401과 같으나 리소스에 대해 인증을 요구하는 프락시 서버를 위해 사용한다.
408 Request Timeout 클라이언트 요청을 완수하기에 시간이 너무 오래 걸리는 경우 서버는 이 상태 코드로 응답하고 연결을 끊을 수 있다.
409 Conflict 요청이 리소스에 대해 일으킬 수 있는 몇몇 충돌을 지칭하기 위해 사용한다. 서버는 요청이 충돌을 일으킬 염려가 있다고 생각될 때 이 요청을 보낼 수 있다. 응답은 충돌에 대해 설명하는 본문을 포함해야 한다.
410 Gone 404와 비슷하나 서버가 한때 그 리소스를 갖고 있었다는 점이 다르다. 주로 웹 사이트를 유지보수하면서 서버 관리자가 클라이언트에게 리소스가 제거되었다는 것을 알려주기 위해 사용한다.
411 Length Required 서버가 요청 메시지에 Content-Length 헤더가 있을 것을 요구할 때 사용한다.
412 Precondition Failed 클라이언트가 조건부 요청을 했는데 그중 하나가 실패했을 때 사용한다. (조건부 요청은 클라이언트가 Expect 헤더를 포함했을 때 발생한다.)
413 Request Entity Too Large 서버가 처리할 수 있는 혹은 처리하고자 하는 한계를 넘은 크기의 요청을 클라이언트가 보냈을 때 사용한다.
414 Request URI Too Long 서버가 처리할 수 있는 혹은 처리하고자 하는 한계를 넘은 길이의 요청 URL이 포함된 요청을 클라이언트가 보냈을 때 사용한다.
415 Unsupported Media Type 서버가 이해하거나 지원하지 못하는 내용 유형의 엔티티를 클라이언트가 보냈을 때 사용한다.
416 Request Range Not Satisfiable 요청 메시지가 리소스의 특정 범위를 요청했는데 그 범위가 잘못되었거나 맞지 않을 때 사용한다.
417 Expectation Failed 요청에 포함된 Expect 요청 헤더에 서버가 만족시킬 수 없는 기대가 있는 경우 사용한다.

500-599 서버 에러 상태 코드

상태코드   사유구절 의미
500 Internal Server Error 서버가 요청을 처리할 수 없게 만드는 에러를 만났을 때 사용한다.
501 Not Implemented 클라이언트가 서버의 능력을 넘은 요청을 했을 때 사용한다. (i.e. 서버가 지원하지 않는 메서드 사용)
502 Bad Gateway 프락시나 게이트웨이처럼 행동하는 서버가 그 요청 응답 연쇄에 있는 다음 링크로부터 가짜 응답에 맞닥뜨렸을 때 사용한다.
503 Service Unavailable 현재는 요청을 처리할 수 없지만 나중에는 가능함을 의미하고자 할 때 사용한다. 만약 서버가 언제 그 리소스를 사용할 수 있을 지 알고 있다면 서버는 Retry-After 헤더를 응답에 포함시킬 수 있다.
504 Gateway Timeout 408과 비슷하지만 다른 서버에게 요청을 보내고 응답을 기다리다 타임아웃이 발생한 게이트웨이나 프락시에서 온 응답이라는 점이 다르다.
505 HTTP Version Not Supported 서버가 지원할 수 없거나 지원하지 않는 버전의 프로토콜로 된 요청을 받았을 때 사용한다.

캐시

웹 캐시는 자주 쓰이는 문서의 사본을 자동으로 보관하는 HTTP 장치다. 웹 요청이 캐시에 도착했을 때 캐시된 로컬 사본이 존재한다면 문서는 원 서버가 아니라 그 캐시로부터 제공된다.

캐시의 장점

  • 원 서버에 대한 요청을 줄여 서버 부하를 줄일 수 있다.
  • 페이지를 먼 곳에서 불러올수록 시간이 많이 걸리는데, 캐시는 거리로 인한 지연을 줄여준다.

불필요한 데이터 전송을 줄인다

여러 클라이언트가 하나의 서버 페이지에 접근할 때, 서버는 같은 문서를 클라이언트들에게 각각 한 번씩 전송하게 된다. 똑같은 바이트들이 네트워크를 통해 계속 반복해서 이동한다. 이 불필요한 데이터 전송은 네트워크 대역폭을 잡아먹고, 전송을 느리게 만들며, 웹서버에 부하를 준다. 캐시를 이용하면 첫 번째 서버 응답은 캐시에 보관된다. 캐시된 사본이 뒤이은 요청에 대한 응답으로 사용될 수 있기 때문에 서버가 중복해서 트래픽을 주고받는 낭비가 줄어든다.

네트워크 병목을 줄인다

캐시는 또한 네트워크 병목을 줄여준다. 많은 네트워크가 원격 서버보다 로컬 네트워크 클라이언트에 더 넓은 대역폭을 제공한다. 만약 클라이언트가 빠른 LAN에 있는 캐시로부터 사본을 가져온다면 캐싱은 성능을 대폭 개선할 수 있을 것이다.

대역폭 병목

https://flylib.com/books/en/1.2.1.70/1/)

갑작스런 요청 쇄도(Flash Crowds)에 대처 가능

캐싱은 갑작스런 요청 쇄도에 대처하기 위해 특히 중요하다. 뉴스 속보와 같은 갑작스런 사근으로 인해 많은 사람들이 동시에 웹 문서에 접근할 때 이런 일이 발생한다.

거리에 의한 지연을 줄인다

대역폭이 문제가 되지 않아도 거리가 문제가 될 수 있다.

보스턴과 샌프란시스코 사이의 거리는 약 4,400킬로미터이다. 이상적인 경우라면 빛의 속도로(300,000킬로미터/초) 신호가 보스턴과 샌프란시스코 사이를 약 15밀리초만에 여행할 것이고 왕복 여행은 30밀리초만에 끝날 수 있을 것이다.

어떤 웹페이지가 20개의 작은 이미지를 포함하는데, 이 모두가 샌프란시스코에 있는 한 서버에 들어있다고 가정하자. 만약 보스턴에 있는 클라이언트가 서버로 동시에 네 개의 커넥션을 열고 그를 유지한다면 다운받을 때 빛의 속도로 인한 지연은 거의 1/4초(240밀리초)가 된다. 만약 서버가 보스턴에서 10,800 킬로미터 떨어진 도쿄에 위치한다면 그 지연은 600밀리초로 커진다. 기계실 근처에 캐시를 설치한다면 문서가 전송되는 거리를 수천 킬로미터에서 수십 미터로 줄일 수 있다.

적중과 부적중

이처럼 캐시는 유용하지만, 캐시가 세상 모든 문서의 사본을 저장하지는 않는다.

캐시에 요엉이 도착했을 때 그에 대응하는 사본이 있어서 그를 이용해 요청을 처리할 수 있다. 이런 경우를 캐시 적중(cache hit)이라 부른다. 만약 대응하는 사본이 없다면 그냥 원 서버로 전달되기만 할 뿐이다. 이를 캐시 부적중(cache miss)이라 부른다.

원 서버 콘텐츠는 변경될 수 있기 때문에 캐시는 반드시 그들이 갖고 있는 사본이 여전히 최신인지 서버를 통해 때때로 점검해야 한다. 이런 ‘신선도 검사’를 HTTP 재검사라 부른다.

캐시는 캐시된 사본의 재검사가 필요할 때 원 서버에 재검사 요청을 보낸다. 컨텐츠가 변경되지 않았다면 서버는 304 Not Modified 응답을 보낸다. 그 사본이 여전히 유효함을 알게된 캐시는 사본이 신선하다고 임시로 다시 표시한 뒤 그 사본을 클라이언트에게 제공한다. 이를 재검사 적중 혹은 느린 적중이라고 부른다. 이는 순수 캐시 적중보다는 느리고 캐시 부적종보다는 빠르다.

HTTP는 캐시된 객체를 재확인하기 위한 몇 가지 도구를 제공하는데, 그 중에서 가장 많이 쓰이는 것은 If-Modified-Since 헤더다. 서버에게 보내는 GET 요청에 이 헤더를 추가하면 캐시된 시간 이후에 변경된 경우에만 사본을 보내달라는 의미가 된다.

GET If-Modified-Since 요청이 서버에 도착했을 때 일어날 수 있는 세 가지 상황이다.

  • 재검사 적중: 만약 서버 객체가 변경되지 않았다면 서버는 HTTP 304 Not Modified 응답을 보낸다.
  • 재검사 부적중: 서버는 HTTP 200 OK 응답을 클라이언트에게 보낸다.
  • 객체 삭제: 서버 객체가 삭제되었다면 서버는 404 Not Found 응답을 돌려보내며 캐시는 사본을 삭제한다.