네트워크/HTTP 완벽 가이드

14. 엔티티와 인코딩 - 인코딩 편

kujaHn 2021. 8. 26. 19:55

Content-Encoding

HTTP는 여러 가지 이유로 인해 컨텐츠 전송 전 인코딩을 합니다.

(전송 시간을 줄이기 위해 압축, 다른 사람이 볼 수 없도록 암호화)

 

1. 인코딩 과정

1st. 웹 서버가 원본 'Content-Type'과 'Content-length' 헤더를 수반한 원본 응답 메시지 생성

 

2nd. 인코딩 서버가 인 코딩된 인코딩 된 메시지를 생성했다면, 'Content-Encoding' 헤더를 인코딩 된 메시지에 추가해, 수신 측 애플리케이션이 이를 확인 해 성공적으로 디코딩할 수 있도록 한다.

   - 이때 'Content-Type'은 인코딩 전의 타입을, 'Content-Length'는 인코딩 후의 길이를 가집니다.

 

3rd. 수신 측 프로그램은 인코딩 메시지를 받아 디코딩을 실시 해 원본을 얻는다.

 

그림1 인코딩 과정

 

2. 인코딩 종류

다양한 인코딩 종류들이 있지만, 대표적인 케이스 몇 개만 설명하겠습니다.

컨텐츠 인코딩 값 설명
gzip 'GNU zip' 인코딩이 적용
compress 유닉스 파일 압축 프로그램인 'compress'가 실행
deflate 'zlib' 포맷으로 압축
identity 어떤 인코딩도 수행되지 않음.
Content-Encoding 헤더가 없는 것과 같다.

위 세 인코딩은 정보의 손실 없이 메시지의 크기를 줄이기 위한 무손실 압축 알고리즘입니다.

이중 gzip가 가장 효율적이고 널리 쓰입니다.

 

3. Accept-Encoding 헤더

기껏 인코딩을 해서 보냈는데 상대측이 해당 알고리즘을 지원하지 않는다면 굉장히 난감할 것입니다.

그림2 지원하지 않는 인코딩

 

그래서 인코딩은 반드시 보내는측과 받는측 모두 알고 있는 방식으로 이루어져야 합니다.

이를 사전에 파악하기 위해서 요청하는 측은 자신이 지원하는 인코딩/디코딩 알고리즘을 알려주는데

이 정보를 Accept-Encoding 헤더에 담아 요청 메시지를 전송합니다.

그림3 Accept-Encoding

 

4. 전송 인코딩 & 청크 인코딩

이전까지는 컨텐츠 인코딩에 대해서 설명을 했습니다. 컨텐츠 인코딩은 컨텐츠 포맷과 관련이 되어 있습니다. (예시 : JPEG는 gzip압축이 어려움)

 

1) 전송 인코딩

이번에는 전송 인코딩에 대해 배워보겠습니다. 전송 인코딩은 구조적인 이유 때문에 네트워크 전송 방법을 바꾸기 위해 적용되는 것이며 컨텐츠 포맷과는 연관이 없습니다.

==========컨텐츠 인코딩==========
HTTP/1.0 200 OK
Content-encoding: gzip
Content-type: text/html
...
[엔티티 본문]	// 이 부분만 인코딩


==========전송 인코딩==========			// 기초적인 헤더 이후 인코딩
HTTP/1.1 200 OK
Transfer-encoding: chunked

3
wleritjdlmfnsdf
5
a

 

전송 인코딩을 제어하고 서술하기 위해 정의된 헤더는 두 가지입니다.

 

Transfer-Encoding 헤더

안전한 전송을 위해 어떤 인코딩이 메시지에 적용되었는지를 수신자에게 알려주는 헤더입니다.

 

TE (Accept-Encoding 역할)

Accept-Encoding과 같은 역할을 하는 헤더입니다.

==========요청 메시지==========
GET/URI HTTP/1.1
Host:www.tistory.com
User-Agent: Mozilla/4.61 [en]
TE: trailers, chunked
...


==========응답 메시지==========
HTTP/1.1 200 OK
Transfer-encoding: chunked
... (인코딩)

다음과 같이 미리 TE헤더에 지원하는 인코딩을 넘겨주어 chunked 인코딩으로 전송 인코딩을 받는 모습입니다.

 

주의사항

모든 전송 인코딩 값은 대소문자가 구별됩니다.

또한 TE 헤더는 어떤 전송 인코딩을 선호하는지 품질값 q(quality value)를 가질 수 있으나 HTTP/1.1로 넘어가며 가질 수 없게 되었습니다.

현재는 청크 인코딩만을 정의하고 있습니다.

 

2) 청크 인코딩

청크 인코딩은 메시지를 일정 크기의 청크 여럿으로 쪼개는 방식입니다. 청크 인코딩을 통해 얻을 수 있는 이점은 지속 커넥션에서 확인할 수 있습니다.

 

지속 커넥션에서 전체 크기를 알 필요가 없어진다.

  지속 커넥션이 아니라면, 클라이언트는 자신이 읽고 있는 본문의 크기를 굳이 알 필요가 없습니다. 그냥 커넥션을 닫는 순간을 본문의 끝으로 판단하고 읽으면 되기 때문입니다. 하지만 지속 커넥션에서는 반드시 'Content-Length' 헤더에 본문의 길이를 담아서 전송해야 합니다. (여러 메시지를 구별하기 위해서)

그러나, 컨텐츠가 서버에서 동적으로 생성되는 경우에는, 본문의 길이를 알아내는 것이 쉽지 않습니다. (거의 불가능)

그래서 전송을 하기 전 본문을 일정한 길이의 여러 청크로 분할을 해서 전송을 하면 위의 문제를 해결할 수 있습니다.

(동적으로 생성되는 본문을 일정 크기의 버퍼에 담은 후 전송.)

그림4 청크 인코딩

각 청크는 16진수로 표기된 길이 값과, CRLF로 분리됩니다. 마지막 청크는 단순히 '본문의 끝'을 의미하기 때문에, 길이가 0인 청크를 전송합니다.

 

Plus. 트레일러

텐츠가 먼저 생성되어야 알 수 있는 헤더 필드들을 담는 헤더입니다. (ex: 'Content-MD5' 헤더)

'Transfer-Encoding', 'Trailer', 'Content-Length'를 제외한 헤더들은 모두 트레일러에 속할 수 있습니다.

 

3) 텐츠 인코딩 + 전송 인코딩

컨텐츠 인코딩과 전송 인코딩은 동시에 사용될 수 있습니다.

먼저 컨텐츠 인코딩을 수행한 후 전송 인코딩을 수행하는 방식입니다. 본문을 디코딩하는 절차는 인코딩의 역순임을 유념합시다!

 

4) 전송 인코딩 주의사항

(1) 전송 인코딩의 집합은 반드시 'chunked'를 포함해야 합니다.

     유일한 예외는 메시지가 커넥션 종료로 끝나는 경우뿐입니다.

 

(2) 청크 전송 인코딩을 사용했다면, 마지막 청크가(Last Chunk) 반드시 존재해야 한다.

 

(3) 청크 전송 인코딩은 반드시 메시지 본문에 한 번 이상 적용되어야 한다.

     - 수신자가 메시지의 전송 길이를 알아낼 수 있게 해 준다.

 

 

5. 델타 인코딩

클라이언트가 어떤 페이지의 만료되거나 최신화가 필요한 사본을 가지고 있으면, 최신 인스턴스를 요청할 것입니다. 

여기서 만약 최신화된 사본이 아주 사소할 정도로 수정된 경우를 생각해봅시다. (예: 10000byte에서 1byte만 수정)

이 1byte만 새로운 인스턴스를 보내면 더할 나위 없이 좋겠지만, 아쉽게도 서버는 전체 인스턴스를 전송할 것입니다.

만약 수정된 일부분만 전송할 수 있다면, 전송속도와 전송량 모두 최적화가 가능할 것입니다. 이를 지원하는 것이 바로 '델타 인코딩'입니다!

 

델타 인코딩은 일종의 인스턴스 조작입니다. 클라이언트는 서버에게 어차피 사소한 내용 정도면 이 정도 인스턴스 조작은 허용할 수 있는데 가능하면 새로운 문서 보내는 대신에 조작을 해라는 요청을 보냄으로써 최적화를 수행합니다.

그림 5 델타 인코딩 요청

헤더 설명
ETag 인스턴스에 대한 유일한 식별자.
If-Match, If-None-Match 헤더에 사용된다.
If-None-Match 다른 버전의 문서를 갖고 있는 경우 그 문서를 요청
A-IM 지원하는 인스턴스 조작의 종류를 가르키는 요청 헤더
IM 요청에 적용된 인스턴스 조작의 종류를 명시하는 서버의 응답헤더
(응답 코드가 266 IM Used 일때 헤더를 전송)
Delta-Base 델타를 생성하기 위해 사용된 기저 문서의 ETag를 명시하는 서버 응답 헤더
(If-None-Match 헤더에 들어있는 ETag와 같아야 함.)

그림6 델타 인코딩 과정