HTTP 캐싱은 브라우저가 서버로부터 받은 파일을 브라우저에 저장하고 있다가 동일한 파일을 요청할 경우 서버에 요청 없이 브라우저에 저장된 파일을 사용하는 기술이다. HTTP 캐싱은 브라우저 캐싱이라는 이름으로도 불린다.
캐싱은 웹페이지의 응답속도에 큰 영향을 미친다. 또한 잘못된 캐싱 전략을 사용하는 경우엔 브라우저가 오래된 캐시 파일을 갖고 있어 업데이트된 파일을 받지 못하는 문제가 발생할 수도 있다. 따라서 캐싱 전략을 잘 이해하고 사용하는 것이 중요하다.
캐싱의 작동 방식
캐싱의 작동 방식을 간단하게 요약하면 다음과 같다.
- 브라우저가 서버에 파일을 요청
- 서버는 요청받은 파일을 응답
- 브라우저는 응답받은 파일을 캐싱
- 브라우저가 같은 파일을 요청할 경우 서버에 요청하지 않고 캐싱된 파일을 사용
여기서 캐싱된 파일을 사용하는 경우에는 서버에 요청을 보내지 않기 때문에 네트워크 비용이 절약되고, 네트워크 통신이 줄어 성능이 향상된다. 하지만 캐싱된 파일이 최신 파일이 아닐 경우 사용자는 최신 파일에 접근하지 못하게 된다. 이를 위해 캐시에는 만료 시간이 존재하며, 만료 시간이 지난 파일은 서버에 요청하여 최신 파일을 받아와야 한다.
캐시 Revalidation
브라우저에 캐싱된 복사본이 만료되었을 때 브라우저는 서버에 '조건부 요청(Conditional Request)'을 보낸다. 이 때 헤더에 If-None-Match 또는 If-Modified-Since 헤더를 사용해 캐시가 여전히 유효한지 판단한다. 서버에서 캐시가 유효하다고 판단하면 304 Not Modified
응답을 보내고, 캐시가 유효하지 않다고 판단하면 200 OK
응답과 함께 업데이트된 응답을 보낸다.
ETag와 Last-Modified
ETag와 Last-Modified 헤더는 캐시 Revalidation을 위해 사용되는 두가지 헤더이다. ETag는 응답의 내용이 변경되었는지 확인하기 위한 헤더이고, Last-Modified는 응답의 최종 수정 시간이 변경되었는지 확인하기 위한 헤더이다.
ETag
ETag, 즉 Entity Tag는 웹 서버의 응답의 특정한 버전을 식별하기 위한 헤더이다. 이 값은 리소스의 내용이 변경될 때마다 변하게 된다. 브라우저는 캐시된 값의 ETag값과 서버에서 받은 ETag값을 비교하여 일치 여부를 확인한다. 서버는 이 값을 최신 리소스의 ETag 값과 비교하여 일치 여부를 확인한다.
Last-Modified
Last-Modified 헤더는 리소스가 마지막으로 수정된 시간을 나타낸다. 이 값은 서버가 리소스를 반환할 때 응답 헤더에 포함된다. 클라이언트는 이 값을 If-Modified-Since 요청 헤더에 포함시켜 서버에 보낸다. 서버는 이 값을 최신 리소스의 Last-Modified 값과 비교하여 일치 여부를 확인한다.
캐시 제어 헤더 (Cache-Control)
Cache-Control 헤더는 캐시의 동작을 제어하는 데 사용되는 헤더이다. 이 헤더는 응답 헤더에 포함되며, 캐시의 동작을 제어하는 다양한 디렉티브를 포함할 수 있다. 형식은 다음과 같다.
Cache-Control: max-age=3600, public
max-age
max-age
디렉티브는 캐시의 유효 기간을 설정한다. 이 헤더는 응답 헤더에 포함되며, 캐시의 유효 기간을 초 단위로 설정한다. 만약 이 값을 0으로 설정한 경우 브라우저는 매번 서버에 revalidation을 요청한다.
no-cache
no-cache
디렉티브는 캐시를 사용하지 않도록 설정한다. 이 헤더는 응답 헤더에 포함되며, 캐시를 사용하지 않도록 설정한다. 이 헤더를 사용하면 브라우저는 브라우저는 매번 서버에 revalidation을 요청한다. max-age=0
으로 설정하는 것과 동작은 동일하다.
no-store
no-store
디렉티브는 캐시를 생성조차 않도록 설정한다. no-cache
디렉티브는 캐싱 자체는 일어나지만 no-store
는 캐싱 자체를 생성하지 않도록 하기 때문에 가장 강력한 옵션이다.
stale-while-revalidate
stale-while-revalidate
디렉티브가 설정되면 캐시가 만료된 이후 stale-while-revalidate
에 설정된 시간만큼 추가로 캐시된 파일을 사용하도록 한다. 이 기간에 접속한 사용자는 이미 만료된 캐시 값을 우선 보게된다. 이 경우 브라우저는 서버에 revalidation을 요청하지만 캐시된 파일을 제공하면서 latency를 줄일 수 있다. 여기서 설정한 시간도 초과하면 그 이후로는 다른 경우와 동일하게 revalidation을 요청한다.
유명한 data fetching 라이브러리인 SWR이 여기서 따온 이름이다.
stale-if-error
stale-while-revalidate
과 비슷하지만 서버에 에러가 났을 경우 캐시된 파일을 사용하도록 한다.
CDN과 캐싱
CDN은 Content Delivery Network의 약자로, 콘텐츠를 전송하는 네트워크를 말한다. CDN은 사용자가 파일을 요청하면 사용자와 가장 가까운 거리에 있는 서버에서 캐싱된 파일을 사용자에게 제공한다. Cache-Control 헤더를 통해 CDN과 브라우저에서 캐싱 전략을 다르게 가져갈 수 있다.
s-maxage
s-maxage 디렉티브는 CDN을 위한 캐시의 유효 기간을 설정한다. 이 헤더는 응답 헤더에 포함되며, 캐시의 유효 기간을 초 단위로 설정한다. 이 값은 max-age보다 우선순위가 높다.
public, private
사용자에 따라 응답 값이 다른 경우 CDN에 캐싱을 하는 것은 부적절하기 때문에 기본적으로 캐시는 private 캐시로 설정된다. 하지만 CDN에서 캐싱을 하고 싶은 경우 public 디렉티브를 사용해야 한다.