프론트엔드에서 성능을 높이기 위해 HTTP 캐시를 전략적으로 사용하면 유리하다. 다만 잘 못 사용할 경우 오히려 사용자에게 유효하지 않은 데이터를 보여주거나 성능을 낮추는 꼴이 되므로 다루기 까다로운 요소 중 하나다.
웹에서 리소스는 원격 서버로부터 HTTP 요청으로 받을 수 있는 파일들을 말하는데, HTML 문서부터 자바스크립트 파일, 이미지, 비디오 등을 포함한다. 브라우저는 이 리소스를 캐싱해 뒀다 잘 활용할 수 있다.
캐시는 데이터를 가져오는
GET
요청에만 다루고 상태코드 200(가져오기 성공), 301(다른 주소로 이동 후 가져옴), 404(가져올 게 없음) 응답을 캐싱할 수 있다.👍 캐시의 이점
- 불필요한 데이터 전송 감소
- 네트워크 병목 감소(네트워크 속도는 가장 느린 속도에 맞춰지므로, WAN → LAN 대역폭 사용)
- 서버 부하 감소
- 거리 지연 감소
🎯 적중과 부적중
캐시 적중은 캐시에 요청이 도착했을 때, 그에 대응하는 사본이 존재하여 요청을 처리하는 경우를 말하고 반대의 경우 캐시 부적중(cache miss)이라 한다.
캐시 사본이 최신인지 점검하는 신선도 검사를 재검사라 하고, 데이터를 최신 상태로 유지해준다. 캐시는 언제든지 사본을 재검사할 수 있으나 이 것은 비효율적이므로 충분히 오래된 경우 재검사를 실시한다. 재검사가 이루어질 떄
적중
과 부적중
, 객체 삭제
가 이루어진다.재검사 결과는 네트워크 응답 코드로 알 수 있는데, 적중은 304, 부적중은 200, 객체 삭제는 404 응답을 보낸다.
재검사 요청은
If-Modified-Since
헤더를 통해 이루어진다.🥦 캐시 사본 신선하게 유지
신선도 여부는 캐시의 나이나 만료일로 얼마나 신선한지 알 수 있다. HTTP 조건부 매서드에 있는 아래 항목으로 신선도 유지를 할 수 있다.
✅ If-Modifyed-Since: 날짜 재검사
주어진 날짜 이후로 수정됐는지 여부를 확인하는 것으로 수정이 됐다면 재검사 부적중에 해당해 새로운 데이터와 함께 네트워크 상태 코드인 200으로 응답이 돌아온다. 만약 데이터가 수정되지 않았다면 적중으로 304 Not Modified 응답으로 캐시 데이터를 사용한다.
✅ If-None-Match: 엔티티 태그 재검사
문서의 태그를 비교해 변경됐다면 새로운 문서를 제공받는다. 엔티티 태그 검사는 강한 검사기로 불리고 문서의 컨텐츠가 바뀔 때마다 ETag 값이 바뀐다. 날짜 검사는 약한 검사기로 불린다.
브라우저 개발자 도구 네트워크 탭에서 ETag 속성이 있는데, 값을 수정해버리면 다음 요청시 캐시를 사용하지 않고 새로운 문서를 서버로 부터 받음
🎮 캐시 제어
HTTP는 문서가 만료되기 전 까지 얼마나 오랫동안 캐싱할지 서버가 설정할 수 있는 여러 방법을 정의한다. 이걸
캐시 제어
라 부르고 아래와 같은 방법이 존재한다.Cache-Control: no-store
캐시가 사용될 수 없음을 의미
Cache-Control: no-cache
재검사 전 까지 캐시를 사용하지 말라는 의미
Cache-Control: no-must-revalidate
캐시 만료 정보를 엄격하게 지키며 반드시 신선한 사본으로 제공해야 한다는 의미
Cache-Control: max-age
캐시의 나이를 설정 신선도를 유지하는 방식
Expires 날짜 헤더
캐시의 만료 날짜를 설정해 신선도를 유지하는 방식
휴리스틱 만료
max-age나 Expires 옵션이 없을 경우 어느것도 없다면 캐시는 경험적으로 캐시 나이를 설정해야하는데, 자주 변경 여부를 판단해 캐싱한다는 의미
📚 Cache Options
fetch
메서드를 기준으로 보자면 기본적으로 표준 HTTP 캐싱을 사용한다. Expires
, Cache-Control
headers, sends If-Modified-Since
등을 수행한다. cache
옵션을 사용하면 HTTP 캐시를 무시하거나 여러 사용법을 미세 조정할 수 있다.- default: 표준 HTTP 캐시 규칙과 헤더 사용(fetch)
- no-store: HTTP 캐시를 완전히 무시하는 모드로, 헤더에 If-Modified-Since, If-None-Match, If-Unmodified-Since, If-Match 또는 If-Range를 설정하면 이 모드가 기본값이 된다. 이 옵션으로 설정할 경우 원격 서버로 데이터를 바로 요청함.
- reload: HTTP 캐시(있는 경우)에서 결과를 가져오지 않고 응답으로 캐시를 채웁니다(응답 헤더가 허용하는 경우)
- no-cache: 캐시된 데이터가 있으면 조건부 요청을 생성하고(서버에 캐시 사용 여부를 물어 봄), 그렇지 않으면 서버에 데이터를 요청 후 응답받은 데이터로 캐시를 채움
- force-cache: 오래된 경우에도 HTTP 캐시의 응답을 사용. HTTP 캐시에 응답이 없으면 일반 HTTP 요청을 수행하고 정상적으로 동작
- only-if-cached"mode"same-origin: 오래된 경우에도 HTTP 캐시의 응답을 사용. HTTP 캐시에 응답이 없으면 오류가 발생
- must-revalidate: 만료된 캐시만 서버에 사용 여부를 묻는 옵션
- public, max-age=<seconds>: 캐시에 유효시간을 지정
위 옵션들은 섞어서 사용할 수 있는데,
no-cache, must-revalidate, no-store
처럼 콤마로 구분해준다. 솔찍히 복잡해서 섞어서 사용하겠나..⭐️ 세부 속성
📌 Age
Age 헤더는 캐시 응답 때 나타나고 max-age 시간 내에서 얼마나 지났는지 알려준다. 만약 30초가 지났다면
Age: 30
으로 응답 헤더에 포함된다.📌 Expires
Cache-Control과 별개로 응답에 Expires값을 추가할 수 있다. 이 값은 응답 컨텐츠 만료 기간을 지정해주는 값으로 max-age가 있는 경우 해당 옵션은 무시된다.
📌 ETag
HTTP 컨텐츠가 바뀌었는지 검사할 수 있는 태그로 같은 주소의 자원이더라도 컨텐츠가 달라지면 ETag가 다르다.
📌 If-None-Match
원격 서버에 ETag가 달라졌는지 검사하고 ETag가 다를 경우 새로운 컨텐츠를 받아오는 의미다. 만약 ETag가 동일하다면 304 Not Modified 응답으로 브라우저는 캐시를 그대로 사용
서버 비용을 내가 낸다고 생각하면 캐시 컨트롤이 잘 될 것이다.