캐시 기본 동작
캐시가 없을 때
1. 브라우저가 요청
2. 서버 응답 1.1m
1. header 0.1m
2. body 1.0m
3. 웹브라우저에 표시
4. 또 요청
5. 또 내려줌 1.1m
6. 또 그림
- 데이터가 변경되지 않아도 계속 네트워크를 통해서 데이터를 다운받아야 함
- 인터넷 네트워크는 매우 느리고 비쌈
- 브라우저 로딩 속도가 느림
- 사용자 경험 느림
캐시가 있을 때
1. 브라우저 요청
2. 서버 응답
1. 서버에서 캐시관련된걸 설정해줘야 함
2. http header
1. cache-control: max-age=60
- 캐시가 유효한 시간
3. 응답 결과를 웹브라우저 캐시 저장소에 저장
4. 또 요청
5. 캐시에서 조회
6. 보여줌
- 캐시 덕분에 캐시 가능 시간동안 네트워크를 사용하지 않아도 됨
- 비싼 네트워크 사용량을 줄일 수 있다
- 브라우저 로딩 속도 매우 빠름
- 사용자 경험 빠름
- ex) 브라우저에서 이미지를 요청 받을 때
- 데이터가 변경되지 않았다면 네트워크를 사용하지 않고 브라우저 캐시에 저장해둔 데이터를 빠르게 가져옴
- 한번 들어갔던데 다시들어가면 굉장히 빠르게 열리는데 이제 캐시 덕분임
캐시 시간 초과
1. 캐시 유효 시간 검증 후
2. 다시 요청
3. 캐시 데이터 받아서 응답 결과를 다시 캐시에 저장
- stale하다
- 캐시 유효 시간이 초과하면 서버를 통해 데이터를 다시 조회하고 캐시를 갱신한다
- 이때 네트워크 다운로드가 다시 발생
그런데 이때 만약 클라이언트랑 서버가 가진 데이터가 같다면 다시 받아야 할까?
- 서버에서 기존 데이터를 변경했다면?
⇒ 다시 보여주면 된다
- 서버에서 기존 데이터를 변경하지 않았다면?
- 캐시 만료 후에도 서버에서 데이터를 변경하지 않았을 경우
- 저장해 두었던 캐시를 재사용할 수 있다 😁
- !단 클라이언트의 데이터와 서버의 데이터가 같다는 사실을 확인할 수 있는 방법 필요!
🐝 검증 헤더와 조건부 요청 헤더
검증 헤더(Validator)
- 캐시 데이터와 서버 데이터가 같은지 검증하는 데이터 (응답 헤더)
- Last-Modified: Thu, 04 Jun 2020 07:19:24 GMT
- ETag: “v1.0”
조건부 요청 헤더
- last-modified와 etag를 활용해서 클라이언트에서 서버로 요청할 때 조건부 요청헤더를 만들어서 보내면 된다 (요청 헤더)
- 검증 헤더로 조건에 따른 분기를 서버에 요청
- 조건이 만족하면
200
- 조건이 만족하지 않으면
304 not modified
- If-Match / If-None-Match: ETag 값 사용
- If-Modified-Since / If-Unmodified-Since: Last-Modified 값 사용
특징
- 캐시 유효 시간이 초과해도 서버의 데이터가 갱신되지 않으면 ⇒
304 Not Modified
+ 헤더 메타 정보만 응답(바디 x)
- 클라이언트는 서버가 보낸 응답 헤더 정보로 캐시의 메타 정보를 갱신
- 클라이언트는 캐시에 저장되어 있는 데이터 재활용
- 결과적으로 네트워크 다운로드가 발생하지만 용량이 적은 헤더 정보만 다운로드
- 매우 실용적
예시1 - Last-Modified, If-Modified-Since
검증 헤더 Last-Modified
- 데이터가 마지막에 수정된 시간
- 클라이언트에서 http 요청을 보낼 때 캐시가 가지고 있는 데이터 최종 수정일이 있다면 조건부 요청 사용
조건부 요청 헤더 If-Modified-Since
- if-modified-since: 데이터 최종 수정일을 서버에 보냄
- 서버에서 데이터가 수정됐는지 안됐는지를 알 수 있다
- 이후에 데이터가
- 변경되지 않았다면 (false)
- ex) 캐시: 2020년 11월 10일 10시 ⇒ 서버: 2020년 11월 10일 10시
- 변경이 안되었으므로 false ⇒ 304 not modified ⇒ 캐시로 redirection
- header 데이터만 전송, body 미포함 (http body가 없음)
- 전송 용량은 header 용량과 같음
- 네트워크 부하가 줄어든다
- 변경 되었다면 (true)
- ex) 캐시: 2020년 11월 10일 10시 ⇒ 서버: 2020년 11월 10일 11시
- 200 ok, body를 포함한 모든 데이터 전송
- 전송 용량은 header 용량 + body 용량
- 캐시를 다시 세팅하거나 불러와서 사용
단점
- 1초 미만 단위로 캐시 조정이 불가능
- 날짜 기반의 정해진 로직 사용
- 데이터를 수정해서 날짜가 다르지만, 같은 데이터를 수정해서 데이터 결과가 똑같은 경우 날짜가 달라졌으므로 변경된 것으로 간주하게 됌
서버에서 별도의 캐시 로직을 관리하고 싶은 경우라면
서버에서 캐시 매커니즘을 컨트롤할 수 있는 방법이 있다
바로 etag다 entity tag
예시2 - ETag, If-None-Match
검증헤더 ETag, 조건부 요청 헤더 If-None-Match
- Etag(Entity Tag)
- 캐시용 데이터에 임의의 고유한 (버전) 이름을 달아둠
- 데이터가 변경되면 이 이름을 바꾸어서 변경함(Hash를 다시 생성)
- ex) 파일을 해시 알고리즘에 넣어서 해시 결과로 받을 수 있는데 파일의 컨텐츠가 같으면 같은 hash 값이 나온다
- 클라이언트에서는 단순하게 ETag만 보내서 같으면 유지, 다르면 다시 받기!
- 매치가 됐다? ⇒ 실패 ⇒ 304 Not Modified ⇒ header만 전송
- 캐시 제어 로직을 서버에서 완전히 관리
- ⇒ 클라이언트는 캐시 메커니즘을 모른다
- 클라이언트는 단순히 이값을 서버에 제공
캐시와 조건부 요청 헤더
cache-control
캐시 지시어(directives)
- cache-control: max-age
- 캐시 유효시간 = 얼마동안 유효할거냐
- 초 단위로 입력 가능(유연함)
- cache-control: no-cache
- 데이터는 캐시해도 되지만 항상 origin 서버에 검증하고 사용
- 원 origin 서버란?
- 중간 캐시 서버(캐시 proxy 서버)에서는 하면 안되고 진짜 뒤에있는 origin 서버까지 가서 해야한다는 뜻
- 항상 조건부 요청을 해서 서버에 이 로컬 캐시 데이터가 바꼈는지 안바꼈는지 검증을 하고 써!
- cache-control: no-store
- 데이터에 민감한 정보가 있으므로 저장하면 안됨
- 메모리에서 사용하고 최대한 빨리 삭제하세요!
- cache-control: public
- 응답이 public cache에 저장되어도 됨
- public cache : 중간에서 공용으로 사용하는 프록시 캐시 서버
- cache-control: private (기본값)
- 응답이 해당 사용자만을 위한 것임
- private cache에 저장해야함
- private cache: 내 웹 브라우저, 로컬에 저장되는 캐시
- ex) 로그인한 사용자 정보
- cache-control: s-maxage
- 프록시 캐시(public 캐시)에만 적용되는 max-age
- age:60(http 헤더)
- origin 서버(원 서버)에서 응답 후 프록시 캐시 내에 머문 시간(초)
pragma
캐시 제어(하위 호환)
- Pragma: no-cache
- http 1.0 하위 호환 때문에 사용하기도 하지만 거의 사용하지 않음
expires
캐시 만료일 지정(하위 호환)
- expires: Mon, 11,
- 캐시 만료일을 정확한 날짜로 지정
- http 1.0부터 사용
- 지금은 더 유연한 cache-control: max-age 권장
- cache-control: max-age와 함께 사용하면 expires는 무시된다
프록시 캐시
원 서버(=origin 서버) 직접 접근
한국 클라이언트에서 미국에 있는 원 서버에 접근하기위해서 0.5초가 걸린다고 가정해보자.
그럼 넘 오래 걸리니까 프록시 캐시를 도입하게 되었다
프록시 캐시 서버를 한국 어딘가에 넣어놓고 요청이 오면 미국에 있는 원 서버에 직접 접근하는게 아니라 한국 서버를 거쳐서 들어오도록 한다
웹 브라우저가 들어오면 원서버가 아닌 프록시 캐시 서버를 요청하면 응답시간이 매우 빨라진다
ex)
유투브 로딩속도
- 이상한 나라의 콘텐츠 엄청 느림 ⇒ 자주 사용하지 않아서 캐시가 없다
- 한국 콘텐츠? 로딩속도 빠름 ⇒ 한국 어딘가에 있는 서버를 이용하는 것임
현재는 다 이런 서비스를 이용하기 때문에 응답이 빠른 것이다
- 보통 첫번째 유저는 느리고 한번 다운로드 받아놓으면 두번째 유저부터는 빠름
- 뒤에서 캐시에 밀어넣는 방법도 있음
여기서
중간에서 공용으로 사용하는 프록시 캐시 서버를 public cache
라고 함
내 웹브라우저나 로컬에 저장되는 캐시를 private cache
라고 함
캐시 무효화
캐시를 적용 안해도 웹브라우저들이 get 요청이 경우에는 임의로 캐시를 해버리기도 한다
그래서 만약 이 페이지는 진짜 캐시가 되면 안된다면! 확실하게 캐시 무효화를 해줘야 한다
cache-control에 확실한 캐시 무효화를 할 수 있는 응답이 있는데 다음을 꼭 넣어줘야 한다!
cache-control: no-cache, no-store, must-revalidate
pragma: no-cache
- cache-control: no-cache
- 데이터는 캐시해도 되지만, 항상 원서버에 검증하고 사용
- cache-control: no-store
- 데이터에 민감한 정보가 있으므로 저장하면 안됨
- cache-control: must-revalidate
- 캐시 만료후 최초 조회 시 원 서버에 검증 해야함
- 원 서버 접근 실패 시 반드시 오류가 발생해야함 - 504 Gateway Timeout
- must-revalidate는 캐시 유효 시간이라면 캐시를 사용함
- Pragma: no-cache
no-cache vs must-revalidate
서버가 에러가 났을 때 처리하는 방식이 다름
- must-revalidate => 항상 오류가 발생
- no-cache => 200으로 내고 오래된 데이터라도 보여준다.(no-cache 정책)
- no-cache나 must-revalidate + etag로 서버에 캐시를 요청한다.
- 프록시 캐시 서버가 봤을 때 처리하지 못하는 거라면 원서버에 요청한다.
- 만약 순간 네트워크가 단절된다면? 원서버에 접근이 불가능할 것이다.
- 그런데 이때 오류를 내지 않고 오래된 데이터라도 보여주도록 하고 싶다면
200
으로 응답을 보내줄 수도 있다. (no-cache 정책)
- 그런데 이때 must-revalidate 옵션이 있다면, 항상 오류가 발생해야 한다 (
504 gateway timeout
)
- 원서버에서 검증한다.
- 원서버는
304 not modified
라고 응답한다
- 캐시 데이터를 사용한다.
REFERENCE