웹캐시

·2024년 3월 12일
0

# HTTP 캐시

HTTP 캐시는 리소스에 대한 특정 요청의 응답을 저장하고, 이를 재사용하는 것을 이야기합니다.

Cache-Control

만약 클라이언트가 이전에 받은 데이터와 똑같은 데이터를 서버에 재요청을 할때 똑같은 통신 과정을 거치게 된다면 이 과정은 굉장한 낭비입니다. 따라서 이러한 낭비를 줄이기 위한 해결책으로 캐시의 개념을 웹브라우저에 그대로 적용한, HTTP에서 제공하는 헤더(Headers)인 Cache-Control 입니다. 이후 HTTP 응답에 포함된 Cache-Control 헤더에 따라 받은 리소스의 생명 주기가 결정됩니다.

# 캐시 검증 Header

- Last-Modified

데이터의 최종 수정 시각을 명시하는 헤더입니다. 응답 HTTP 헤더에는 원본 서버가 리소스가 마지막으로 수정되었다고 생각하는 날짜와 시간이 포함되어 있습니다. 이 헤더는 리소스가 이전에 저장된 리소스와 동일한지 확인하기 위한 유효성 검사기로 사용됩니다. 

단점은 날짜 기반의 로직을 사용하기 때문에 1초 미만(0.x초) 단위로 캐시 조정이 불가능합니다. 데이터를 수정해 날짜는 다르지만, 데이터 내용은 같은 경우 캐시가 일어나지 않습니다.

또한 스페이스, 주석과 같이 데이터에 큰 영향이 없는 변경이 있을 경우에도 캐시를 유지하고 싶은 경우가 있습니다. 하지만 이와 같은 경우에 서버에서 별도의 캐시 로직을 관리할 수 없습니다.

- ETag

서버에서 생성하는 HTTP 컨텐츠가 바뀌었는지를 검사할 수 있는 태그입니다.

# 조건부 요청 Header

- If-Modified-Since

클라이언트의 요청시 사용되며 캐시 데이터의 Last-Modified 값이 들어갑니다. 서버의 데이터 최종 수정 시각캐시 데이터의 최종 수정 시각을 비교하여 데이터 수정 여부를 확인하기 위하여 사용합니다.

- If-None-Match

서버에게 ETag가 달라졌는지 검사해 ETag가 다를 경우에만 컨텐츠를 새로 내려주라는 뜻입니다. 만약 ETag가 같다면 서버는 304 Not Modified를 응답해서 캐시를 그대로 사용합니다.


# no-cache

캐시하되, 매번 서버에 확인

app.get("/no-cache", (req, res) => {
  res.set("Cache-Control", "no-cache"); 
  res.send("This response is not cached.");
});

똑같은 요청을 두번 보내보았습니다.

두번째 요청은 실제로 데이터를 가져오는 요청이 아니라, Etag를 비교하는 요청입니다.

요청상태크기
첫번째200281B
두번째304203B

304의 의미

304 응답은 HTTP 본문을 포함하지 않습니다. 요청 헤더의 크기는 203B, 캐싱을 통해 본문 응답값 총 85B 를 절약한 것을 알 수 있습니다.

# no-cache vs must-revalidate

must-revalidate는 유효한 캐시는 재사용 가능하고 만료된 캐시만 서버에 재검증 요청을 보냅니다. 캐시 무효화를 설정할때 보통 no-cache 와  must-revalidate 를 같이 설정합니다. must-revalidate 를 사용하는 이유는 no-cache에 의해 원 서버에 검증 요청을 보내는 도중, 원 서버와 프록시 서버의 연결이 끊어져 검증이 불가능한 경우 504 오류를 발생시키기 위해서입니다. 왜냐하면 몇몇 프록시 캐시 서버에서는 원 서버에 접근이 불가능해질 경우에검증을 거치지 않고 이전의 캐시 데이터를 반환하기 때문입니다.

# no-store

전혀 저장하지 않음

app.get("/no-store", (req, res) => {
  res.set("Cache-Control", "no-store");
  res.send("This response is not stored at all.");
});

no-store 값은 캐시를 절대로 해서는 안 되는 리소스일 때 사용합니다. 캐시를 만들어서 저장조차 하지 말라는 가장 강력한 Cache-Control 값입니다. no-store를 사용하면 브라우저는 어떤 경우에도 캐시 저장소에 해당 리소스를 저장하지 않습니다.

# no-setting

아무 설정도 하지 않은 API (Express.js)

app.get("/no-setting", (req, res) => {
  res.send("Hello, World!");
});

이번에도 똑같은 요청을 두번 보내보았습니다.

요청상태크기
첫번째200240B
두번째304177B

저는 Test를 위해 Express로 간단한 서버를 열었습니다. Express.js는 모든 응답에 Etag를 자동으로 생성해 헤더에 추가합니다. 즉 프레임워크의 기본 설정입니다.

app.set("etag", false);

해당 옵션을 통해 etag 생성을 막아줄수 있습니다.


# 프록시 캐시

프록시는 클라이언트와 본서버 중간에 위치하는 서버 대리자입니다. 원서버와 클라이언트간의 거리가 멀어도 빠른 요청과 응답이 가능한 것은 프록시 캐시 덕분입니다. 클라이언트에서 사용하고 저장하는 캐시를 private캐시, 서버의 캐시를 public 캐시 라고 합니다.

웹브라우저 캐시와 프록시 서버 캐시가 분리되어 운용되는 만큼 위에서 배운Cache-Control HTTP 헤더도프록시 전용 캐시 설정을 해야 합니다.

Cache-Control: public 또는 private

public이면 공유 캐시(또는 중개 서버)에 저장해도 된다는 뜻이고 private이면 브라우저같은 특정 사용자 환경에만 저장하라는 뜻입니다.


레퍼런스

https://hahahoho5915.tistory.com/33
https://inpa.tistory.com/entry/HTTP-🌐-웹-브라우저의-캐시-전략-Cache-Headers-다루기
https://velog.io/@neity16/HTTP-7-캐시와-조건부-요청-Last-Modified-ETag
https://blog.naver.com/jukrang/221612657825
https://www.zerocho.com/category/HTTP/post/5b594dd3c06fa2001b89feb9

profile
My Island

0개의 댓글