콘텐츠 협상

HTTP에서 콘텐츠 협상(Content negotiation)이란 동일한 URI에서 리소스의 서로 다른 버전을 제공하기 위해 사용하는 메커니즘으로, 사용자 에이전트가 사용자에게 제일 잘 맞는 것이 무엇인지(예를 들어, 문서의 언어, 이미지 포맷 혹은 컨텐츠 인코딩에 있어 어떤 것이 적절한지)를 명시할 수 있습니다.

콘텐츠 협상의 원칙

우리는 특정 문서를 리소스라고 부릅니다. 클라이언트가 리소스를 내려받길 원할 경우, 그것의 URL을 사용하여 요청합니다. 서버는 리소스가 제공하는 여러 변형들 중 하나를 선택하기 위해 이런 URL을 사용하며 (각각의 변형을 프레젠테이션이라고 부릅니다) 클라이언트에게 해당 리소스의 특정 프레젠테이션을 반환합니다. 프레젠테이션들에 더해, 전체 리소스들은 특유의 URL을 가집니다. 리소스가 호출됐을 때 특정 프레젠테이션을 선택하는 방법은 콘텐츠 협상에 의해 결정되며 클라이언트와 서버 간의 협상에는 몇 가지 방식이 존재합니다.

가장 잘 맞는 프레젠테이션의 결정은 다음 두 개의 메커니즘 중 하나를 통해 이루어집니다:

  • 클라이언트가 보내는 특정 HTTP 헤더를 이용하는 방법(서버 주도 협상 혹은 주도적인 협상)으로, 특정 종류의 리소스에 대한 표준 협상 방법입니다.
  • 서버에 의해 전달되는 300 (다중 선택) 혹은 406 (허용되지 않음) HTTP 응답 코드를 이용하는 방법(에이전트 주도 협상 혹은 리액티브 협상)으로, 폴백 메커니즘으로써 사용됩니다.

수년 간, 투명한 콘텐츠 협상Alternates 헤더와 같은 다른 콘텐츠 협상 제안들이 제안되어 왔습니다. 그런 제안들은 관심을 끄는데 실패했고 결국 버려졌습니다.

서버 주도 콘텐츠 협상

서버 주도 콘텐츠 협상 혹은 주도적인 협상에 있어서, 브라우저(혹은 사용자 에이전트라면 어떤 다른 종류든지)는 URL을 이용해 몇 개의 HTTP 헤더를 전송합니다. 이런 헤더들은 사용자의 우선적인 선택을 나타냅니다. 서버는 그것들을 힌트로써 사용하며 내부 알고리즘은 클라이언트로 서브하기 위한 최선의 컨텐츠를 선택하게 됩니다. 이 알고리즘은 서버 특유의 것이며 표준으로 정의된 것은 아닙니다. 예를 위해, Apache 2.2 협상 알고리즘을 참고하시기 바랍니다.

HTTP/1.1 표준은 서버 주도 협상을 시작하는 표준 헤더 목록(Accept, Accept-Charset, Accept-Encoding, Accept-Language)을 정의하고 있습니다. 엄밀히 말하자면 User-Agent (en-US)이 리스트 내에 없긴 하지만, 해당 헤더는, 좋은 관례가 아니라고 판단될지라도, 때때로 요청된 리소스의 특정 프레젠테이션을 전송하는데 사용되기도 합니다. 서버는 실제로 콘텐츠 협상에 있어 어떤 헤더가 사용될 지 (더 엄밀히 말하자면 연관된 응답 헤더) 가리키기 위해 Vary 헤더를 사용하므로 캐시는 최적으로 동작하게 됩니다.

이것과 더불어, 클라이언트 힌트라고 부르는 헤더들을 이용 가능한 헤더 목록에 추가하려는 실험적인 제안도 존재합니다. 클라이언트 힌트는 사용자 에이전트가 실행 중인 기기의 종류가 무엇인지를 알려줍니다(예를 들어, 데스크톱 컴퓨터인지 모바일 기기인지)

서버 주도 콘텐츠 협상이 리소스의 특정 프레젠테이션에 동의하기 위한 가장 일반적인 방법이긴 하지만, 몇 가지 결점을 가지고 있습니다:

  • 서버는 브라우저에 대한 전체적인 지식을 가지고 있지 않습니다. 클라이언트 힌트 확장이 있더라도, 서버는 브라우저의 수용 능력에 대한 완벽한 정보를 가지고 있진 않습니다. 클라이언트가 선택하는 리액티브 콘텐츠 협상과는 다르게, 서버 선택은 항상 다소 임의적입니다.
  • 클라이언트에 의한 정보는 상당히 장황하며(HTTP/2 헤더 압축은 이런 문제를 완화시킵니다) 사생활 침해에 대한 위협을 가지고 있습니다(HTTP 핑거프린팅).
  • 주어진 리소스의 몇몇 프레젠테이션이 전송되므로, 샤드된 캐시들은 덜 효율적이며 서버 구현은 좀 더 복잡해집니다.

Accept 헤더

Accept 헤더는 에이전트가 처리하고자 하는 미디어 리소스의 MIME 타입을 나열합니다. 그것은 MIME 타입을 쉼표로 구분한 목록이며, 각각 품질 인자와 함께 나열되어 있으며, 다른 MIME 타입 사이의 상대적인 선호도를 나타내는 파라메터이기도 합니다.

Accept 헤더는 브라우저나 다른 에이전트에 의해 정의되며 HTML 페이지 혹은 이미지나 비디오 또는 스크립트들을 가져오는 것처럼, 컨텍스트에 따라 다양해질 수 있습니다: 주소창에 입력된 문서를 내려 받을 때와 <img>, <video> 혹은 <audio> (en-US) 엘리먼트를 통해 링크된 요소를 내려받을 때가 다릅니다. 브라우저는 그들이 판단하기에 가장 적절한 헤더의 값을 마음껏 사용할 것입니다. 일반적인 브라우저를 위한 기본적인 값 (en-US)의 전체 목록을 참고하기 바랍니다.

Accept-CH 헤더 Experimental

참고: 이것은 클라이언트 힌트라고 불리는 실험적인 기술의 일부로 현재 크롬 46과 그 이후 버전에서만 구현되어 있습니다.

실험적인 Accept-CH (en-US)는 적합한 응답을 선택하기 위해 서버가 사용할 수 있는 설정 데이터를 나열합니다. 유효한 값들은 다음과 같습니다:

의미
DPR 클라이언트 기기의 픽셀 비율(ratio)을 가르킵니다.
Viewport-Width CSS 픽셀에서의 레이아웃 뷰포트를 가리킵니다.
Width 물리적인 픽셀에서의 리소스 너비를 가리킵니다(다시 말해 이미지의 고유 사이즈).

Accept-Charset 헤더

Accept-Charset 헤더는 사용자 에이너트가 어떤 종류의 캐릭터 인코딩을 이해할 수 있는지를 서버에게 알려줍니다. 전통적으로, 그것은 브라우저에 대해 각 지역을 위해 서로 다른 값을 설정하는데 사용되어 왔습니다. 예로 들자면, 서부 유럽 지역을 위해서는 ISO-8859-1,utf-8;q=0.7,*;q=0.7처럼 설정했었습니다.

UTF-8이 현재는 잘 지원되고 있고 인코딩 캐릭터로써 선호하는 방식이 되고 있는 상황에서, 더 적은 설정 기반의 엔트로피(불확실성)를 통해 좀 더 나은 개인정보 보호를 보장하기 위해, 대부분의 브라우저들은 Accept-Charset 헤더를 생략하고 있습니다. Internet Explorer 8, Safari 5, Opera 11 그리고 Firefox 10은 이 헤더를 폐기했습니다.

Accept-Encoding 헤더

Accept-Encoding 헤더는 수용 가능한 (압축을 지원하는) 컨텐츠 인코딩을 정의하고 있습니다. 값은 인코딩 값의 우선 순위를 가리키는 q 인자 목록(예를 들어, : br, gzip;q=0.8)입니다. 기본값 identity는 가장 낮은 우선 순위입니다(선언된 것이 없는 경우).

HTTP 메시지 압축은 웹 사이트의 성능을 높이는 가장 중요한 방법이며, 전송 데이터의 크기를 줄여주며 가용할 수 있는 대역폭을 더 좋은 상태로 만들어줍니다; 브라우저는 항상 이 헤더를 전송하며 서버는 그것을 받아들이고 압축을 사용하도록 구성되어 있어야 합니다.

Accept-Language 헤더

Accept-Language 헤더는 사용자가 선호하는 언어를 가리키는데 사용됩니다. 그것은 품질 인자를 가진 값 목록입니다("de, en;q=0.7"). 기본 값은 사용자 에이전트의 그래픽 인터페이스 언어와 관련하여 설정되지만, 대부분의 브라우저들은 다른 언어 설정을 허용합니다.

구성 기반의 엔트로피의 증가로, 사용자 판별에 수정된 값을 사용할 수 없고, 그것을 수정하는 것은 권장되지 않으며 웹 사이트는 사용자의 실제 요구를 반영하기 위해 이 값을 신뢰할 수 없습니다. 이 헤더를 통해 감지된 언어가 좋지 않은 사용자 경험을 유발할 수도 있으므로 사이트 설계자는 해당 헤더 값을 맹신해서는 안 됩니다:

  • 사이트 설계자들은 서버에서 선택한 언어가 아닌 다른 언어를 선택할 수 있는 방법을 제공해야 합니다. 예를 들자면 사이트 상에 언어 메뉴를 제공하는 것이죠. 대부분의 사용자 에이전트들은 사용자 인터페이스 언어에서 차용된 값을 Accept-Language 헤더의 기본값으로 제공하는데, 최종 사용자는, 예를 들어서 인터넷 카페 같은데서, 대게 어떻게 하는지 몰라서, 혹은 그것이 가능한지 몰라서, 그것을 수정하지 않습니다.
  • 사용자가 서버가 선택한 언어를 재정의하고 나면, 사이트는 더 이상 언어 감지를 사용해서는 안되며 명시적으로 선택된 언어를 인정해야 합니다. 다시 말하자면, 사이트의 엔트리 페이지에서만 이 헤더를 사용하여 적당한 언어를 선택해야 합니다.

User-Agent 헤더

참고: 컨텐츠를 선택함에 있어 이 헤더를 정당하게 사용한다고 할지라도, 사용자 에이전트가 지원하는 것이 무엇인지를 정의하려고 이 헤더에 의지하는 것은 나쁜 습관으로 간주됩니다.

User-Agent (en-US) 헤더는 요청을 전송하는 브라우저를 식별하게 해줍니다. 이 문자열은 공백 문자로 구분된 *제품 토큰(product tokens)*과 코멘트 목록을 포함합니다.

*제품 토큰(product token)*은 Firefox/4.0.1처럼 브라우저 이름 뒤에 '/'와 버전 번호가 오는 이름입니다. 사용자가 에이전트가 원하는 만큼 많은 수의 이름이 올 수 있습니다. 코멘트는 둥근 괄호를 경계로 하는 자유 문자열입니다. 분명한 것은 괄호가 문자열 안에서는 사용될 수 없다는 것입니다. 코멘트의 내부 형식은 표준으로 정해진 것은 없으나 몇몇 브라우저들은 ';'로 구분하여 그 안에 몇 개의 토큰을 넣습니다.

Vary 응답 헤더

이전에 봤던 클라이언트에 의해 전송되는 Accept-* 헤더들과는 달리, Vary HTTP 헤더는 웹 서버에 의한 응답 내로 전달됩니다. 이 헤더는 서버 주도 콘텐츠 협상의 과정 중에 서버에 의해 사용되는 헤더들의 목록을 나타냅니다. 이 헤더는 결정 기준 캐시를 알리기 위해 필요하므로 사용자에게 잘못된 컨텐츠를 제공하는 일을 방지하는 동안 캐시가 가동되게 허용하도록 캐시를 복제할 수 있습니다.

특별한 값인 '*'은 서버 주도 콘텐츠 협상이 적합한 컨텐츠 선택을 위해 헤더로 전달되지 않은 정보도 사용한다는 것을 의미합니다.

Vary 헤더는 HTTP 1.1 버전에서 추가되었으며 캐시를 적절하게 동작하도록 허용하는 일이 불필요합니다. 캐시는, 에이전트 주도 콘텐츠 협상과 함께 동작하도록 하기 위해, 전송된 컨텐츠를 선택하도록 서버에 의해 사용되었던 기준이 어떤 것인지 알 필요가 있습니다. 그런 방법으로, 캐시는 알고리즘을 재연할 수 있으며 서버에 추가적인 요청없이도 수용 가능한 컨텐츠를 직접 서브할 수 있게 될 것입니다. 확실히, 캐시는 무슨 요소가 뒤에 있는지 알 수 없으므로, '*' 와일드카드는 현재 일어나고 있는 일들로부터 캐시되는 것을 방지합니다.

에이전트 주도 협상

서버 주도 협상은 몇 가지 불리한 점 때문에 고통을 줍니다: 그것은 확장하기가 용이하지 않습니다. 협상 내에서 사용하는 기능 당 한 가지 헤더가 존재해야 합니다. 만약 스크린 크기, 해상도 혹은 또 다른 치수를 사용하고자 한다면, 새로운 HTTP 헤더가 반드시 만들어져야 합니다. 헤더의 전송은 반드시 모든 요청 상에서 이루어져야 합니다. 이것은 몇몇 헤더들에 있어서는 그리 문제될 것은 아니지만, 그런 헤더들이 결국 증가하여, 메시지 사이즈가 성능에 악영향이 끼치는 상황이 올 수도 있습니다. 정확한 헤더가 전송되면 전송될수록, 불확실성도 더욱 더 전송되어, 좀 더 많은 HTTP 흔적과 그와 관련된 개인정보들이 남게 됩니다.

HTTP의 초창기부터, 프로토콜은 또 다른 협상 유형을 허용했습니다: 에이전트 주도 협상 혹은 리액티브 협상. 이 협상에서, 애매모호한 요청과 맞닥뜨렸을 경우, 서버는 사용 가능한 대체 리소스들에 대한 링크를 포함하고 있는 페이지를 회신하게 됩니다. 사용자는 해당 리소스들을 표시하고 사용하려는 리소스를 선택하게 됩니다.

불행하게도, HTTP 표준은 그 과정을 쉽게 자동화하는 것을 막는, 사용 가능한 리소스 중에서 선택하도록 허용하는 페이지의 형식을 명시하지 않고 있습니다. 게다가 서버 주도 협상으로의 회귀로, 이 방법은 스크립팅, 특히 JavaScript 리다이렉션과 함께 거의 항상 사용됩니다: 협상 기준을 점검하고 난 뒤에, 스크립트는 리다이렉션을 실행합니다. 두번째 문제는 실제 리소스를 가져오는데 한 개 이상의 요청이 필요로 해서, 사용자에 대한 리소스 효용성이 떨어진다는 것입니다.