이 글은 강의 : 김영한님의 - "[모든 개발자를 위한 HTTP 웹 기본지식]"을 듣고 정리한 내용입니다. 😁😁
캐시와 조건부 요청과 관련된 헤더들과 동작들에 대해서 알아보자.
개요
클라이언트가 서버에 요청하는 자원(resource)은 단순한 텍스트부터, 이미지, 영상, 파일까지 다양하다. 그 중에서는 용량이커서 큰 네트워크 비용을 부담해야하는 자원들이 있고, 변경 가능성이 잦지 않은 자원들이 있다.
이런 자원들을 매 번 요청시마다 새로 다운받는건 큰 네트워크 비용 부담이다.
그래서 나온 것이 캐시라는 개념으로 이런 자원들에 대해 브라우저 캐시에 우선 저장해두고, 해당 자원이 변경되지 않았다면, 서버에서 다운받지않고 브라우저 캐시에 저장해놓은 자원을 다시 사용한다는 개념이다.
(캐시와 조건부 요청 헤더)
캐시는 캐시 지시어라는 헤더필드를 이용하여 제어할 수 있다.
Cache-Control이라는 **헤더 필드에 원하는 값을 지정해서 캐시의 수명이나 로직을 정의한다.**
cache-control -> 캐시 지시어
🎃 Cache-Control: max-age
cache-control: max-age=60
: 60초간 유효하다.🎃 Cache-Control: no-cache
🎃 Cache-Control: must-revalidate
504 Gateway Timeout
🎃 Pragma: no-cache
캐시의 유효시간이 만료된 경우 원 서버에 다시 자원을 요청하는데, 만약 아직도 자원이 변경되지 않았다면, 여전히 자원을 새로 반환할게 아니라 변경되지 않았다고 응답해 브라우저 캐시의 자원을 재활용하는게 효율적이다. 이를 돕기 위해 검증헤더가 사용되며, 이러한 검증 헤더는 두 가지 방식이 있다.
검증 헤더!
해당 헤더에 자원의 마지막 수정 시간 정보를 작성함으로써 두 정보를 비교해 캐시 활용 여부를 결정할 수 있다.
🎃 최초 자원 요청시 서버측에서는 유효시간(60초)와 Last-Modified로 해당 자원(star.jpg)가 마지막으로 수정된 날짜를 UTC 기준으로 작성하여 보내준다.
🎃 클라이언트측은 응답결과를 캐시에 저장하여 해당 자원은 60초간은 서버에 요청하지 않고 브라우저 캐시에 저장된 자원을 활용한다.
🎃 유효시간이 만료되면 다시 서버에 자원을 요청하는데 이 때 if-modified-since라는 검증헤더에 서버측으로부터 받았던 Last-Modfied 날짜를 작성해서 전달한다.
🎃 서버측에서는 해당 자료의 최종 수정일과 클라이언트가 전송한 최종 수정일을 비교하여 변경이 되었을 경우 새로운 자원을 다시 최종변경일과 유효시간과 함께 전송하고, 만약 아직 자원이 변경되지 않았다면 다시 새로운 유효시간(max-age)를 작성해 304 Not Modified 상태코드를 Message Body 없이 반환하여 기존 브라우저 캐시의 자원을 활용하도록 한다.
Body 미포함이 핵심. -> 리다이렉션 헤더만 전송하면 돼
주의점: 해당 검증 헤더는 1초 이하(0.5초,...)의 시간은 저장하지 못하기 때문에, 실제로 0.5초뒤에 자료가 변경되었다 하더라도 감지할 수 없다. 또한, 데이터를 수정했다가 원복한 경우에도 이를 감지하지 못한다.
캐시를 오로지 서버에서 컨트롤함으로써 블랙박스처럼 정보를 은닉하고자 할 때 사용하는 검증 헤더이다. Last-Modified 대신 ETag(Entity Tag)라는 검증 헤더를 이용하여 서버측에서 지정한 임의의 버전(혹은 이름)등을 제공하여 관리한다.
🎃 서버측에서는 ETag 라는 검증 헤더에 자원에 맞는 값을 작성하여 반환한다.
🎃 클라이언트의 캐시에서는 해당 ETag 값을 저장한다.
🎃 해당 자원의 유효시간(max-age)이 만료되어 다시 서버에 요청을 할 경우 저장해뒀던 ETag를 If-None-Match에 작성하여 보낸다.
🎃 서버측에서는 해당 자원의 현재 ETag값과 전달받은 Etag값을 비교하여 같을 경우 304 Not Modified 상태코드를 반환하고, 값이 다를 경우 새로운 ETag와 함께 200 OK 상태코드를 반환한다.
이 방식은 ETag 생성 로직을 온전히 서버에서 관리하기에 Last-Modified처럼 변경날짜같은게 외부에 노출되지 않는다. 그리고 클라이언트에서는 이런 생성로직이나 캐시 메커니즘에 대해서도 이해할 필요 없이 그저 값을 사용하기만 하면 된다.
Cache-Control: no-cache, no-store, must-revalidate Pragma: no-cache
🎃 확실한 캐시 무효화 응답
🎃 no-cache , must-revalidate 혼용해야 하는 이유
정리하면,
no-cache로 매번 원 서버에 검증을 요청하도록 하고, must-revalidate로 접속이 실패할 경우 무조건 오류를 발생하도록 해서 캐시를 사용하지 않도록 한다. 거기에 Pragma 헤더를 추가해 1.0 이하 버전의 하위호환성도 적용한다.