종종 하나의 URL이 여러 리소스에 대응 할 필요가 있는 경우가 있다.
예를 들어 어떤 사이트에 클라이언트의 국적이 다양 할 경우
URL
은 동일하더라도 여러 국적의 클라이언트에게 맞는 언어로 구성된 페이지 (리소스)를 제공해야 하기 때문이다.
여러 국적 별로 URL
을 여러개로 분활해서 만들어두는 것보다 하나의 URL
에서 클라이언트에게 맞게 리소스를 제공하는 것이 더욱 효과적이다.
위 예시를 그대로 가져와 사용하면
서버는 여러 국적의 클라이언트에게 맞는 리소스를 준비해뒀다고 가정해보자
영어 버전 , 한국어 버전 , 프랑스어 버전 .. 등등 말이다.
이 때 클라이언트에게 적합한 리소스를 제공하는 방법은 3가지 존재한다 .
기술 | 설명 | 장점 | 단점 |
---|---|---|---|
클라이언트 주도 | 클라이언트는 요청 헤더에서 선호하는 콘텐츠 유형 또는 언어를 지정하며, 서버는 그에 따라 응답합니다. |
|
|
서버 주도 | 서버는 클라이언트의 요청 헤더를 확인하고 사용 가능한 리소스를 기반으로 최적의 콘텐츠 유형 또는 언어로 응답합니다. |
|
|
투명 ( transparent ) |
중개자(프록시 서버 등)가 클라이언트와 서버 간의 콘텐츠 협상을 중재하고, 최적의 콘텐츠 유형 또는 언어를 결정하여 클라이언트에게 전달합니다. |
|
|
서버 입장에서 가장 간편하고 클라이언트 입장에서도 본인에게 가장 맞는 리소스를 받을 수 있는 방법이다.
클라이언트가 서버측에게 요청을 보내면
서버는 클라이언트에게 자신이 가지고 있는 리소스들 리스트를 보낸다.
이건 한국어 버전 .. 이건 영어 버전 .. 이건 프랑스어 버전 .. 뭐할래 ?
그럼 클라이언트는 가장 자신에게 적합한 버전을 선택하여 다시 요청을 보낸다.
이는 클라이언트가 직접 선택하기 때문에 가장 본인에게 맞는 자료를 받을 수 있지만
리소스 리스트를 받고, 자신에게 맞는 리소스를 선택한다는 것에서 불필요한 트랜잭션이 2번 이상 일어난다는 단점이 존재한다.
또한 언어 별 리소스가 다르다면 www.example.com
은 한국어 버전일 때
www.example.com/english
는 영어버전 , www.example.com/french
는 프랑스어 버전 등 리소스 별 url
이 달라진다는 단점이 존재한다.
이 방법은 클라이언트 주도 협상과 다르게 불필요한 트랜잭션이 일어나지 않는다.
첫 번째 요청 떄 클라이언트는 본인이 받고자 하는 리소스의 언어와 같은 메타정보를
헤더에 담아 서버에 요청한다.
서버는 클라이언트의 메타 요청 자료를 보고 리소스를 선택해 제공한다.
다만 이는 클라이언트의 요청에 적합한 리소스가 존재하지 않을 때
클라이언트 : 제주어로 된거 주세요
서버가 직접 적합한 리소스를 선택해 제공해야 한다는 단점이 있다.
메타 요청을 보내는 헤더는 다음과 같다.
헤더 | 설명 | 예제 |
---|---|---|
Accept | 클라이언트가 이해하거나 처리할 수 있는 미디어 유형을 지정합니다. | text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8 |
Accept-Language | 응답에 대한 선호하는 자연어를 나타냅니다. | en-US, en;q=0.5 |
Accept-Charset | 응답에 대해 허용되는 문자 집합을 지정합니다. | utf-8, iso-8859-1;q=0.5 |
Accept-Encoding | 콘텐츠 압축에 대해 클라이언트가 이해할 수 있는 인코딩을 전달합니다. | gzip, deflate, br |
클라이언트는 다음과 같은 헤더를 서버에게 보낸다.
이것들은 기존 엔터티 헤더 와 비슷하거나 동일한 양식이지만
현재 말하는 헤더들은 협상 단계에서 쓰이는 헤더들이다.
표에 열거된 협상 헤더들을 클라이언트에게 받으면 서버는 요청과 적절한 엔터티를 선택하고
협상 내용과 관련된 엔터티 헤더들을 작성해 제공한다.
헤더 | 설명 | 예제 | 사용 용도 |
---|---|---|---|
Content-Type | 엔터티의 미디어 타입을 나타냅니다. | application/json; charset=utf-8 |
응답 엔터티 헤더 |
Content-Language | 엔터티의 자연어를 나타냅니다. | en-US |
응답 엔터티 헤더 |
Content-Encoding | 엔터티의 콘텐츠 압축에 대한 인코딩을 나타냅니다. | gzip |
응답 엔터티 헤더 |
나는 영어버전, 한국어버전 , 프랑스어버전만 존재하는데 클라이언트가 스페인어만 주세요
이러고 있다면 말이다.
이런 경우 서버에게 차선책을 선택 할 수 있도록 품질값(quality value , q값) 이 존재한다.
각 프로토콜은 클라이언트의 선호마다 q
값을 정의 할 수 있다.
q
값은 0~1 사이의 값을 갖는다.
예를 들어
Accept-Langauge : en;q = 0.5 , fr;q=0.0 , kr;q=1.0
이란 클라이언트의 헤더가 있다면
클라이언트 : 한국어 버전 줘 , 그런데 없다면 영어 버전도 괜찮아 프랑스어 버전은 필요 없어
처럼 요구하고 있는 것이다.
Accept
헤더들 말고도 사용되는 협상헤더만약 유저가 사용하는 브라우저가 구식일 경우엔 버전에 맞춘 리소스를 제공해야 할 것이다. 이를 위해
User-Agent
를 협상 헤더로 서버가 사용하기도 한다.
투명 협상은 서버와 클라이언트간의 협상을 중개 프록시를 통해 서버 주도 협상을 하면서 서버와의 트랜잭션은 낮추는 방법이다.
이는 프록시에서 리소스들을 캐싱해두고 사용자들에게 협상 헤더에 맞는 리소스들을 담아 보내주는 것이다.
이를 위해 서버는 프록시가 마치 서버와 같은 기준으로 협상을 하도록 교육해야 한다.
이를 위해 처음 프록시 서버가 서버로부터 리소스를 받을 때
서버는 리소스와 함께 Vary
헤더에 서버가 리소스를 보내기 전 살펴보는 헤더들은 이것들이야 하고 알려준다.
alternate
)만일 캐시 서버에 저장된 리소스가 A
라고 해보자 . 이것은 한국어 버전으로 된 것이다.
이 때 영어권 클라이언트가 영어로 된 리소스를 달라고 서버에게 요청을 보냈다.
요청은 서버까지 가지 않고 프록시 서버에서 캐치한다.
이후 프록시 서버는 협상 헤더를 보고 서버 측에 영어로 된 버전을 달라고 요청한다.
이를 B
리소스라고 해보자
그럼 프록시 서버는 영어권 이용자에게는 B
리소스를 보내도록 캐싱해둔다.
이 때 기존 가지고 있던 리소스가 아닌, 협상에 맞춰 준비된 리소스들을 Variant
나 alternate
라고 한다.
이 때 생성되는 Variant
들은 모두 서버측에서 프록시 서버에게 보낸 Vary
헤더의 내용에 맞춰 생성될 것이다.
만약 서버가 Vary
헤더에 Accept-Language : 뭐뭐뭐..
이렇게 보냈다면
프록시 서버는 Accept-Language
별 수 많은 variant
들을 캐싱해둘 것이다.
만약 요청에 맞는
variant
가 없다면 서버에 있느냐 묻고, 만약 서버에도 없다면q
값을 보고 보낼 것이다. 아니면 존재하지 않는다는 에러 상태 코드를 보내던지 말이다.
트랜스 코딩은 여러 협상 헤더에 맞는 리소스들을 미리 만들어두는 것이 아니라
협상 헤더에 맞춰 리소스를 변경하는 것을 의미한다.
포맷 변환은 데이터를 클라이언트가 온전히 볼 수 있도록 다른 포맷으로 변환하는 과정을 의미한다.
예를 들어 내 리소스는 높은 성능을 요구하는 데스크탑에서만 볼 수 있을 때
클라이언트가 성능이 낮은 데스크톱이나 모바일에서 서버에 리소스를 요청했다고 가정해보자
그렇다면 우리는 원본 리소스를 보내면 클라이언트는 우리가 보낸 정보를 모두 인식 할 수 없다.
그렇기에 클라이언트에 맞게 리소소들을 다운그레이드 시킨 후 보내줘야 한다.
높은 해상도의 이미지를 낮춘다거나 , 애니메이션 효과를 제거한다거나 .. 등등
포맷 변환을 통해 우리는 클라이언트에게 우리가 보내고자 하는 정보를 온전히 보낼 수 있다.
콘텐츠 주입의 가장 대표적인 예시는 광고 삽입이다.
요청한 리소스의 내용이나 클라이언트의 쿠키 등을 통해 리소스에
사용자 맞춤 광고를 추가하여 효과적인 홍보를 할 수 있다.