본문 바로가기
개발

[HTTP 완벽 가이드] 15장 엔터티와 인코딩 15.4~15.10

by soyooooon 2025. 1. 22.
반응형

15.4 미디어 타입과 차셋(Charset)

Content-Type 헤더 필드는 엔터티 본문의 MIME 타입을 기술한다. 텍스트, 이미지, 오디오 등의 주 미디어 타입과 빗금(/), 그리고 미디어 타입을 더 구체적으로 서술하는 부 타입으로 구성된다. 예시로는 text/html, text/plain, image/gif, multipart/byteranges 등이 있다.

  • 텍스트 매체를 위한 문자 인코딩 : ex. 엔터티 비트 집합을 텍스트 파일의 글자들로 변환하기 위한 charset 매개변수
  • 멀티파트 미디어 타입 : 서로 붙어있는 여러 개의 메시지를 포함하며 하나의 복합 메시지로 보내짐 ex. 폼 제출 시, 문서 일부분을 전달하는 응답을 할 때
  • 멀티파트 폼 제출 : 멀티파트 본문은 여러 다른 종류와 길이로 채워진 폼을 허용한다.

15.5 콘텐츠 인코딩

  1. 웹 서버가 원본 Content-Type과 Content-Length 헤더를 포함한 원본 응답 메시지를 생성
  2. 콘텐츠 인코딩 서버가 인코딩 메시지를 생성. Content-Type 유지, Content-Length 변경
  3. Content-Encoding 헤더를 인코딩된 메시지에 추가
  4. 수신 측 프로그램은 전달 받은 메시지를 디코딩 후 원본 확인

gzip, compress, deflate 인코딩은 전송되는 메시지의 크기를 정보의 손실 없이 압축해주는 무손실 압축 알고리즘이다. 클라이언트는 자신이 지원하는 인코딩의 목록을 Accept-Encoding 요청 헤더를 통해 전달한다. 만약 Accept-Encoding 헤더가 없다면 어떤 인코딩이든 받아들일 수 있는 것으로 간주한다. 클라이언트는 각 인코딩에 Q 값을 매개변수로 더할 수 있는데, 가장 원치 않음을 의미하는 0.0에서 가장 선호함을 의미하는 1.0까지의 범위 안에서 선호도를 나타낼 수 있다.

15.6 전송 인코딩과 청크 인코딩

전송 인코딩은 네트워크를 통한 안전한 전송을 위해 존재했다. HTTP에서 전송된 메시지의 본문이 문제를 일으킬 수 있는 이유는 몇 가지만 존재하며, 그 중 대표적인 두 가지는 '알 수 없는 크기'와 '보안'이다.

  • 알 수 없는 크기 : 몇몇 게이트웨이 애플리케이션과 콘텐츠 인코더는 콘텐츠를 먼저 생성하지 않고서는 본문의 최종 크기를 판단할 수 없는데, 이 서버들은 사이즈를 알기 전에 전송을 시작하려고 한다.
  • 보안 : 보안 문제를 해결하기 위해 공용 전송 네트워크로 메시지 콘텐츠를 보내기 전에 전송 인코딩을 사용하여 알아보기 어렵게 뒤섞을 수 있지만 SSL과 같은 유명한 전송 계층 보안 방식이 있어 전송 인코딩 보안은 흔히 사용되지는 않는다.

전송 인코딩을 제어하고 서술하기 위해 정의된 헤더

  • Transfer-Encoding : 어떤 인코딩이 메시지에 적용되었는지 수신자에게 알려준다.
  • TE : 어떤 확장된 전송 인코딩을 사용할 수 있는지 서버에게 알려주기 위해 요청 헤더에 사용한다.

청크 인코딩

청크 인코딩은 전송 인코딩의 한 형태이다. 청크 인코딩은 메시지를 일정 크기의 청크 여럿으로 쪼갠 후 서버가 각 청크를 순차적으로 보낸다. 청크 인코딩에서는 동적으로 본문이 생성되면서 서버는 그중 일부를 버퍼에 담은 뒤 한 덩어리를 크기와 함께 보낼 수 있다. 이 단계를 계속 반복하다가 서버가 크기가 0인 청크를 통해 본문이 끝났음을 알리고 다음 응답을 위해 커넥션을 열린 채로 유지할 수 있다.

청크 인코딩은 HTTP 응답 헤더로 시작하고, 청크 스트림이 온다. 각 청크는 길이 값과 각 청크에 대한 데이터를 담고 있으며 이 두 가지는 CRLF로 분리된다. 청크 데이터의 길이는 바이트 단위로 측정되며 CRLF는 길이에서 제외한다.

클라이언트는 서버가 청크 인코딩을 받아들일 수 있을지 모르기 때문에 411 Length Required 응답으로 거절되는 것을 대비해야 한다.

 

청크 메시지에 트레일러를 추가할 수 있는 경우

  • 클라이언트의 TE 헤더가 트레일러를 받아들일 수 있음을 나타내는 경우
  • 트레일러가 응답을 만든 서버에 의해 추가되었으며, 트레일러의 콘텐츠는 클라이언트에서 이해하고 사용할 필요가 없는 선택적인 메타데이터로 무시하고 버려도 되는 경우

전송 인코딩이 메시지 본문에 적용될 때 규칙

  • 전송 인코딩 집합은 메시지가 커넥션의 종료로 끝나는 경우를 제외하고는 반드시 'chunked'를 포함해야 한다.
  • 청크 전송 인코딩이 사용되었다면 메시지 본문에 적용된 마지막 전송 인코딩이 존재해야 한다.
  • 청크 전송 인코딩은 반드시 메시지 본문에 한 번 이상 적용되어야 한다.
  • 서버가 이해할 수 없는 전송 인코딩된 메시지를 받았다면, 서버는 501 Unimplemented 상태 코드로 응답해야 한다.

15.7 시간에 따라 바뀌는 인스턴스

웹 객체는 정적이지 않으며 같은 URL 이더라도 시간에 따라 다른 버전의 객체를 가리킬 수 있다. 인스턴스 조작 중 대표적인 두 가지가 범위 요청과 델타 인코딩인데, 두 가지 모두 클라이언트가 자신이 갖고 있는 리소스의 사본이 서버가 갖고 있는 것과 같은지 판단하고 상황에 따라 새 인스턴스를 요청할 수 있는 능력을 가질 것을 요구한다.

15.8 검사기와 신선도

문서가 클라이언트에서 만료되면 클라이언트는 서버에게 최신 사본을 요구해야 한다. 만약 서버에서도 문서가 변겨오디지 않았다면 다시 받을 필요가 없다. 클라이언트는 조건부 요청을 통해 서버에게 자신이 갖고 있는 버전을 말해주고 사본 버전이 더 이상 유효하지 않을 때만 사본을 보내달라고 요청할 수 있다.

신선도

서버는 클라이언트에게 Expires나 Cache-Control 헤더를 통해 얼마나 오랫동안 콘텐츠를 캐시하고 그것이 신선하다고 가정할 수 있는지에 대한 정보를 준다. Expires를 사용할 경우 시계를 항상 동기화시켜야 하는데, 네트워크 시간 프로토콜과 같은 시계 동기화 프로토콜을 실행하지 않았을 가능성이 양쪽 모두에게 있기 때문에 상대시간을 이용하여 만료를 정의하는 것이 더 효율적일 수 있다. Cache-Control은 문서가 서버를 떠난 후로부터의 총 시간을 초 단위로 정하기 때문에 더 정확한 결과를 말해줄 수 있다. Cache-Control 헤더에 동반될 수 있는 지시자들은 no-cache, no-store, max-age, max-stale, min-fresh, only-if-cached, public, private 등이 있다.

조건부 요청과 검사기

조건부 요청은 평범한 HTTP 요청 메시지이지만, 특정 조건이 참일 경우에만 수행된다. 조건부 요청은 'If-'로 시작하는 조건부 헤더에 의해 구현되며 그 예로는 If-Modified-Since/Last-Modified, If-Unmodified-Since/Last-Modified, If-Match/ETag, If-None-Match/ETag가 있다. 이 중 / 뒤에 적힌 Last-Modified, ETag는 HTTP에 의해 사용되는 두 개의 주요한 검사기이다. Last-Modified는 정확도가 최대 1초에 불과하기 때문에 약한 검사기로 간주되며, ETag는 매 변경마다 구분되는 값을 넣어두기 때문에 강한 검사기로 구분된다.

15.9 범위 요청

범위 요청을 이용하면 HTTP 클라이언트는 받다가 실패한 엔터티를 일부 혹은 범위로 요청함으로써 다운로드를 중단한 시점에서 재개할 수 있다.

Range: bytes=4000-

위 예제에서 클라이언트는 처음 4,000바이트 이후의 부분을 요청하고 있다. 또한 Range는 여러 범위로 요청을 하기 위해 사용될 수 있는데, 어떤 문서에 대한 다운로드 시간을 줄이기 위해 동시에 여러 서버에 접속해서 서로 다른 범위를 요청하는 클라이언트의 형태가 있을 것이다. 이 경우 응답은 멀티파트 본문과 Content-Type: multipart/byteranges 헤더와 함께 하나의 엔터티로 돌아온다. 서버는 클라이언트에게 자신이 Range를 받아들일 수 있는지를 응답에 Accept-Range 헤더를 포함하여 알려줄 수 있다.

하지만 인스턴스 조작의 일종이기 때문에 클라이언트와 서버가 같은 버전의 문서를 갖고 있을 때만 의미가 있다는 것을 생각하고 주의해야 한다.

15.10 델타 인코딩

델타 인코딩은 객체 전체가 아닌 변경된 부분에 대해서만 통신하여 전송량을 최적화하는 HTTP 프로토콜의 확장이다. 이는 인스턴스 조작의 일종이다.

  1. 클라이언트는 페이지의 어떤 버전을 갖고 있는지 서버에게 말해주어야 한다. (최신 버전에 대한 델타를 받아들일 의사가 있음을 의미)
  2. 클라이언트는 현재 버전에 델타를 적용하기 위해 어떤 알고리즘을 알고 있는지 서버에게 말해주어야 한다.
  3. 서버는 클라이언트가 갖고 있는 버전을 갖고 있는지, 최신 버전과 클라이언트 버전 사이의 델타를 어떻게 계산할 것인지 체크해야 한다.
  4. 서버는 델타를 계산해서 클라이언트에게 보내주고 클라이언트에게 보내고 있음을 알려주면서 페이지의 최신 버전에 대한 새 식별자를 명시해야 한다.
  5. 클라이언트는 자신이 갖고 있는 버전에 대한 유일한 식별자를 *If-None-Match 헤더에 담는다. (*If-None-Match: 갖고 있는 최신 버전의 페이지가 이것과 같은 ETag를 가지고 있지 않다면 최신 버전의 페이지를 보내달라)
  6. 서버는 최신 버전이 클라이언트가 갖고 있는 버전과 다르다면 클라이언트에게 그 페이지의 최신 버전 전체를 보낼 것이다.

클라이언트는 A-IM(Accept-Instance-Manipulation) 헤더를 보내서 자신이 페이지에 대한 델타를 받아들일 수 있음을 알려줄 수도 있다. 클라이언트는 자신이 알고 있는 알고리즘을 A-IM 헤더 안에 명시한다. 서버는 클라이언트에게 요청한 객체에 대해 객체 자체가 아닌 인스턴스 조작을 보내고 있음을 말해주는 226 IM Used 응답 코드, 델타를 계산하기 위해 사용된 알고리즘을 명시한 IM 헤더, 새 ETag 헤더, 델타 계산할 때 기반이 된 문서의 ETag를 지정한 Delta-Base 헤더를 되돌려준다.

클라이언트는 A-IM 헤더를 사용해서 자신이 받아들일 수 있는 인스턴스 조작의 종류를 명시할 수 있는데, 그 예로는 vcdiff, diffe, gdiff, gzip, deflate, range, identit 등이 있다. 문서는 클라이언트에게 반환되기 전에 압축률을 극대화하기 위해 여러 인스턴스 조작을 거칠 수 있다. 예를 들어 vcdiff 알고리즘에 의해 델타를 생성 후 gzip 알고리즘을 이용해 압축될 수 있다.

 

델타 인코딩은 전송 시간을 줄일 수 있지만 구현하기가 까다로울 수 있다. 변경이 잦고 많은 사람이 접근하는 페이지의 경우, 문서를 제공하는데 걸리는 시간이 줄어드는 대신 서버가 문서의 과거 사본을 모두 유지하기 위해 디스크 공간을 더 늘려야 하기 때문에 전송량 감소로 얻은 이득을 무의미하게 만들 수 있다.

반응형