카테고리 없음

HTTP 총 정리 마지막 편

유승혁 2022. 3. 1. 23:29

 안녕안녕안녕 오늘 드디어 마지막 시간예요! 지난 글에서 약속한 헤더에서의 더 많은 이야기를 다루어 보겠습니다!

 

 1. 협상

 협상 헤더는 당연히 표현 헤더에 포함이 되죠! 이 협상은 주로 클라이언트가 요청메시지를 보낼 때 이루어 집니다. 같은 리소스에 대해 다른 버전이 필요할때 쓰이죠. 심지어 리소스 범위를 변경 해 버릴 수도 있습니다.(좀 어렵지만 아래 예시를 들겠어요.) 그럼 협상 헤더 종류를 시작으로 자세히 알아 보겠습니다.

 - Accept : 클라이언트가 선호하는 미디어 타입 전달

 - Accept-Charset : 클라이언트가 선호하는 문자 인코딩

 - Accept-Encoding : 클라이언트가 선호하는 압축 인코딩

 - Accept-Language : 클라이언트가 선호하는 자연 언어(영어, 한국어)

 자세한 예시로서 자연 언어로 이야기 하자면, 구글에 아마존 사이트를 한국 국적설정 해서 입력하면 헤더 필드에 Accept-Language: ko 가 자연스레 붙어 아마존 한국 버전이 오는 식이랍니다.

 하지만 만약 애초에 독일 사이트이고! 그 서버가 지원하는 언어는 독일어와 영어인데다가, 독일 사이트이니 독일어가 디폴트 값인데 클라이언트가 한국어 버전을 요구한다면? ㅜㅜ 그나마 할 줄 아는 영어로 받는게 나은데 독어로 나오겠죠.. 이럴 때를 대비해 우선순위라는 것이 있다!

 우선순위는 0~1사이로 부여하며 높을 수록 우선순위가 높습니다. ;q=? 식으로 덧붙입니다. 예를 들면 아래 사진과 같지요.

위 사진에 따르면 ko-KR이 1로서 우선순위가 젤 높고 ko가 0.9로 2등. en-US가 0.8로 3등 마지막으로 en이 0.7로 꼴찌입니다.

우선순위의 또 다른 특징은 구체적인 것이 우선순위가 높다는 것!

리소스 범위에 대한 이야가 바로 Accept 해더 입니다.

Accept: text/*, text/plain, text/plain;format=flowed, */* 이 네 가지 중 가장 우선순위가 높은 것은 세 번째가 되겠죠. 가장 구체적이니!

 

 2. 전송방식

 크게 네 가지로 나뉩니다. 단순 전송, 압축 전송, 분할 전송, 범위 전송. 하나하나 알아가보죠.

 - 단순 전송 : 지금 껏 많이 해오던 것입니다. Content-Length: (n), 메시지 바디 부분의 바이트 단위로 길이를 알려주는 헤더입니다. 

 - 압축 전송 : Content-Encoding 헤더에 어떤 종류로 압축 했는 지를 나타냅니다. 그렇다면 그 밑에 오는 헤더인 Content-Length의 값이 좀 작아지겠죠.

 - 분할 전송 : Transfer-Encoding: chunked를 붙입니다. 그럼 아래 사진 처럼 공백이나 엔터를 기준으로 줄이 나뉘고 마지막 줄에는  널 값과 CRLF 가 필요합니다.

 - 범위 전송 : 전체 영역에서 일부만 필요할 때 쓰입니다. Range와 Content-Range 헤더가 있고 쓰임새는 비슷합니다. 형식은 아래 두 사진이면 해결 될거 같네요.

 

 3. 쿠키

 과자 아닙니다. 많이들 익숙 하죠. 많은 사이트에서 쿠키 허용하나 마냐로 배너가 자주 뜹니다. 첫 글에서 다루었듯이 HTTP 는 무상태 프로토콜 입니다. 그래서 지난 요청과 응답에 대한 정보를 서버는 기억하지 않지요. 그래서 클라이언트는 자신의 정보를 기억해서 요청 메시지에 보내는데 만약 항상 쿼리 파라미터나 바디에 넣게 된다면 정보가 길어질 뿐 아니라 브라우저를 완전히 종료하고 다시 열면... 첨부터 다시해야하는 아뿔싸 불쌍사가 생깁니다. 이 해결 법을 로그인을 예시로 들겠어요. 가장 대표적인 쿠키 예시가 되기 때문입니드아. 

아예 맨 처음에 로그인 할때 서버에서 응답 메시지에 Set-Cookie 헤더로 ID, PW의 키 값을 던져 줍니다. 암호화 되어있으니 키 값을 주죠. 이 키 값을 각 클라이언트의 쿠키 저장소에 저장 하고 있습니다.

그 후에 로그인 할때 클라이언트가 Cookie 헤더에 key=value 값으로 각 암호화된 값들을 저장소에서 꺼내 주어 클라이언트가 새로운 요청 메시지를 보낼 때 이전 정보를 포함해서 보냅니다.

 

3.1 쿠키 특징

 - 생명주기 : Set-Cookie: expires=Sat, 26-Dec 와 같은 식으로 만료일을 주거나 Set-Cookie: max-age=600 처럼 초 단위로 시간을 주기도 합니다. 물론 0이나 음수를 주면 해당 쿠키를 삭제하라는 뜻 이에.

 또한 세션 쿠키와 영속 쿠키 개념이 있는데 만료 날짜를 생략하면 브라우저 종료 시 까지만 유지하는 것이 세션 쿠키, 만료 날짜를 위 처럼 입력하면 브라우저 종료해도 살아있는 것이 영속쿠키.

 - 도메인 : domain=example.org 를 명시하면 명시 된 도메인과 그 서브 도메인에 쿠키를 만들고 접근 할 수 있다. 즉, dev.example.org 처럼 서브 도메인에도 쿠키 접근이 가능 하다는 뜻.

 - 경로 : 도메인과 비슷한 느낌. path=/home 으로 지정 하면 home 하위의 모든 파일과 디렉터리에 접근 할 때에 쿠키 설정 접근이 가능하다.

 - 보안 : 넘어가자. 검색 고고

 

 4. 캐시

 캐시란? 간간히 언급 했지만 지대로 말을 하자면 별 모양 사진을 인터넷에서 다운 받았다라고 해보자. 이때 10Mbps를 차지한다고 했을 때 0.1초 뒤에 다시 다운 받을 때도 10Mbps를 차지하겠지? 근데 만약에 별 사진에 딱히 바뀐 점 이 없다면 굳이 10Mbps 를 사용할 이유가 하등 없다. 그래서 우리에겐 브라우저 캐시라는 저장소가 있어서 "최근에" 다운 받았던 파일에 대해 다시 다운 받지 않고 그냥 그 저장소에서 꺼내어 쓴다.

 아니 그럼 "최근에" 라는 기준이 무엇이드냐?

cache-control: 헤더와 함께 max-age=60과 같이 하여 60초 간 똑같은 요청이 들어오면 캐시 저장소를 사용하는 것이다. 당연히 60초가 지난 뒤에 또다시 요청이 들어오면 새롭게 다운 받고 60초간 별 사진은 캐시가 유효하게 된다.

 

 4.1 60초 뒤에도 사진이 안바뀐다면?

 물론 60초 뒤에는 별표가 동그라미로 바뀌었는지 확신 할 수 없으니 서버에게 확인은 해 보아야 한다. 하지만 만약 별표 그대로라는 것을 확인 하면 캐시 저장소에 있는 것을 써도 되지 않능가? 추가로 별표를 다운 받는 것보다 변경 안됐어요~ 하는 메시지를 받는 것이 훨씬 값 싼 통신이 될 듯 하니 이를 적극 활용한 헤더가 있다.

 이를 조건부 헤더라고 하는데 대표적으로 Last-Modified: (년/월/일/시/분/초) 즉, 데이터 수정 일이 등록 되어 있어 별 사진을 클라이언트가 다운 받을 때 이 수정 일을 기억하고 있다가 별표 사진을 달라는 요청 메시지를 다시 보낼 때 if-modified-since: (년/월/시/분/초) 헤더를 보내면 응답 메시지 스타트 라인이 HTTP/1.1 304 Not Modified 가 명시되어 리다이렉션하고 밑에 cache-control 헤더와 Last-Modified헤더의 내용이 명시되어 캐시 저장소에 별 사진을 60초간 다시 유효하게 하여 이 60초 안에 다시 요청하면 캐시 저장소의 별표를 사용한다.

 

 4.2 아직의 문제들

 1초 미만으로 바뀐 사진은 인지하지 못한다는 것! 데이터를 수정해서 날짜는 바뀌었지만 수정해서 내용이 똑같은 경우! 주석 처럼 변경해도 딱히 상관이 없는 경우! 이 세 경우에는 그냥 저냥 쓰면 되지만, 좀 더 효율을 따질 수 없을 까? 해서 나온 것이 ETag 헤더와 If-None-Match 헤더 이다.

 클라이언트가 서버에게 별 사진 달라 하면 ETag: aaaa 와 같이 헤더를 추가한다. 이때 "aaaa"는 서버에서 지정 할 수도 있고 대게 해시값을 적용한다. 그렇게 되면 이제 받은 쪽에서 그 값을 캐시 저장소에 저장하고 별 사진을 60초 후에 다시 요청 할 때 If-None-Match: aaaa을 포함하여 보낸다. 만약 변동 사항이 없으면 서버의 ETag 값도 aaaa일터이니 위에서 Last-Modified 처럼 304 코드와 ETag 값, max-age 값을 주어 60초간 다시 그 캐시는 유효하게 된다.

 

 4.3 캐시 제어 헤더

 캐시 자체를 할 수 없게 하기도 하고 캐시를 하더라도 원 서버에 확인 하고 캐시 값을 사용하게 하기도 한다. 천천히 알아보자.

Cache-Control: 헤더 뒤에 쿼리 형식으로 오는 지시어 들이다.

- max-age: 캐시 유효 시간, 초 단위

- no-cache: 데이터는 캐시해도 되지만, 항상 원 서버에 검증하고 사용. 여기서 원 서버란 컴네 글에서도 다루 었듯이 local DNS나 authoritiative DNS 같은 중간 서버가 아닌 리소스가 존재하는 정확한 그 서버를 칭합니당.

 - no-store: 데이터에 민감한 정보가 있으므로 저장 하지 마시오!

 

 5. 프록시 캐시

  원 서버를 위에서 언급했지요. 예를 들어 미국에서 온 별 사진을 한국의 A 서버에 저장을 해 놓으면 다른 사람이 그 별 사진이 필요해 지면 굳이 원 서버에서 안 받고 한국의 A 서버에서 다운 받으면 상당히 이득이겠지요? (더 가까우니 비용이 적습니다) 이런 중간 서버에 저장 하는 것을 프록시 캐시 또는 public 캐시라고 합니다. 반대로 개인 클라이언트의 캐시는 private 캐시라고 하지요. 이 또한 Cache-Control 해더와 함께 아래 쿼리 형식으로 오는 지시어들이 있다요.

 - public : 응답이 public 캐시에 저장 되어도 됨

 - private : 응답이 private 캐시에 저장 되어야만 하니 프록시 캐시에 저장 되면 안됨

 - s-maxage : 프록시 캐시 전용 max-age

 

 6. 캐시 무효화

 캐시 무효화는 말 그대로 어디에도 캐시 저장 하지 말라는 뜻이고 하는 방법은 간단합니다. Cache-Control: no-cache, no-store, must-revalidate와 Pragma: no-cache 이렇게 두 개의 헤더를 추가하면 끝!

 각각을 따져보자면 먼저 Pragma는 중간 서버들이 HTTP 1.0 하위 버전 일 수 있어서 추가하는 것이고 나머지는 다루었으니 must-revalidate 만 다루어 보자면 캐시 만료 후에 최초 조회 시 원 서버에 검증 해야 한다는 의미인데 no-cache와 무슨 차이냐?! 라고 물어본다면, no-cache의 경우 예를 들어 별 사진을 클라이언트-프록시-원 서버 순으로 사진을 받는다고 했을 때 캐시 만료 후 검증 할 때 프록시~원 서버에서 네트워크 단절 같은 경우 불상사가 생기면 프록시 서버의 구현에 따라서 그냥 이전 캐시된 별 사진을 보내 줘 버릴 수 있기 때문에 만약 서버에서는 이미 별 사진이 아닌 네모 사진을 들고 있게 되면 서버와 클라이언트 사이의 통신 오류가 발생 한다. 이를 애초에 방지하기 위해 must-revalidate는 원 서버 접근 불가시 504 코드와 Gateway Timeout을 보내 오류 코드를 보내어 잘못된 상황을 인지한다. 또한 저번에 다루었듯이 5XX 는 서버 문제이니 해당 요청을 다시 자동으로 하게 하는 뭐 그런 식으로 하면 되겠지요~~

 

 마무리

 HTTP 부분 끝! 이제 HTTP의 전반적인 컨셉을 이해했으니 다음 스프링 공부는 좀 더 깊이 있게 이를 접목해서 공부 할 수 있을 거 같아 너무너무 기대 된다. 내일 부터 바로 적용하러 가보겠드아! 금방 또 소식 올리니 끼따려용₩₩₩