캐시(Cache)는 다운로드 받은 데이터나 값을 미리 복사해 놓는 임시 장소를 뜻하며, 데이터에 접근하는 시간이 오래 걸리는 경우나 값을 다시 계산하는 시간을 절약하고 싶은 경우에 사용한다. 우선 캐시를 사용하지 않을 때의 예시를 들어보겠다.
서버에서 logo.jpg라는 이미지를 받아오는 요청을 보낸다고 해보자. 첫 번째 요청에서는 이미지를 통째로 받아오게 된다. 이때 HTTP 헤더의 용량이 0.1M, 이미지의 용량이 1.0M라면 응답의 총 용량은 1.1M이 될것이다.
문제는 두 번째 요청부터이다. 완전히 똑같은 파일을 또 다시 받아오는 일이 발생하기 때문이다. 전에 받아두었던 파일을 재사용할 수 있다면, 첫 번째 요청을 보냈을 때처럼 1.1M의 응답을 통째로 받아올 필요없이 HTTP헤더의 용량인 0.1M만 받아도 될 것 같은데 말이다.
이럴 때 캐시를 활용하면 이러한 리소스 낭비를 막을 수 있습니다. 캐시를 사용하게 되면 어떤 일이 일어나는지 확인해보자.
이번에는 서버에서 응답을 보내줄 때 이미지 파일과 함께 헤더에 Cache-Control
을 작성해서 보내준 것을 볼 수 있다. 값은 60으로, 해당 이미지 파일이 60초동안 유효하다는 것을 의미한다.
이제 두 번째 요청부터는 캐시를 우선 조회하게 된다. 캐시에 데이터가 존재하면서 아직 60초가 지나지 않아 유효하다면 캐시에서 해당하는 데이터를 가져와서 사용하게 된다.
만약 유효 시간이 60초가 지났다면? 서버에서 다시 이미지를 받아오게 된다. 캐시 유효 시간 동안은 똑같은 데이터를 다시 받아올 필요가 없어지는 것이다.
이렇게 브라우저 캐시를 활용하면 다음과 같은 효과를 볼 수 있다.
캐시를 활용하면 캐시가 유효한 시간 동안은 캐시에 저장해놓은 데이터를 재활용할 수 있다는 것을 알게 되었다. 하지만 다음과 같은 경우는 어떨까?
캐시 유효 시간은 지났지만, 서버에서 다시 받아와야하는 파일이 캐시에 저장되어 있는 파일과 완전히 동일한 경우를 생각해보라. 이때도 똑같은 파일을 다시 받아와야하는 경우가 발생한다. 이럴 땐 유효 시간이 지났다고해도 굳이 똑같은 파일을 다시 받아올 필요 없이 서버의 파일과 캐시의 파일이 동일한지 확인해서 재사용하면 더 효율적이지 않을까?
다행히도 이런 상황에서 사용할 수 있는 HTTP 헤더들이 존재한다. 바로 캐시 검증 헤더와 조건부 요청 헤더이다.
캐시에 저장된 데이터와 서버의 데이터가 동일한지 확인하기 위한 정보를 담은 응답 헤더
Last-Modified
: 데이터가 마지막으로 수정된 시점을 의미하는 응답 헤더로, 조건부 요청 헤더인 If-Modified-Since
와 묶어서 사용한다.
Etag
: 데이터의 버전을 의미하는 응답 헤더로, 조건부 요청 헤더인 If-None-Match
와 묶어서 사용한다.
캐시의 데이터와 서버의 데이터가 동일하다면 재사용하게 해달라는 의미의 요청 헤더
If-Modified-Since
: 캐시된 리소스의 Last-Modified 값 이후에 서버 리소스가 수정되었는지 확인하고, 수정되지 않았다면 캐시된 리소스를 사용한다.
If-None-Match
: 캐시된 리소스의 ETag 값과 현재 서버 리소스의 ETag 값이 같은지 확인하고, 같으면 캐시된 리소스를 사용한다.
캐시 검증 헤더와 조건부 요청 헤더를 어떻게 사용하는지 조금 더 자세히 살펴보겠다.
Last-Modified
와 If-Modified-Since
-첫 번째 요청을 보내고 응답을 받으면서 캐시 유효 시간이 60초인 이미지 파일을 같이 받아온다. 이 때, 서버의 파일이 마지막으로 수정된 시간을 의미하는 `Last-Modified` 헤더에 담긴 내용도 캐시에 함께 저장한다.
- 캐시 유효 시간인 60초를 초과한 후에 두 번째 요청을 보낸다고 해보자. 비록 유효 시간이 지났어도 해당 데이터를 재사용해도 되는지 확인하기 위해서 “이 날짜 이후로 데이터 수정이 있었니? 없었다면 캐시에 저장해놓은 데이터를 재사용해도 괜찮을까?”라는 뜻의 요청 헤더 `If-Modified-Since` 를 작성하고 캐시에 함께 저장해놓았던 `Last-Modified` 값을 담아 요청을 보낸다. 이 값을 이용해 서버 데이터의 최종 수정일과 캐시에 저장된 데이터의 수정일을 비교한다. 두 데이터가 동일한 데이터라면 최종 수정일이 같아야 한다.
- 서버와 캐시의 데이터가 동일한 데이터임이 검증되었다면 서버는 “데이터가 수정되지 않았음”을 의미하는 `304 Not Modified` 라는 응답을 보내주고, 캐시 데이터의 유효 시간이 갱신되면서 해당 데이터를 재사용할 수 있게 된다.
Etag
와 If-None-Match
- 첫 번째 요청을 보내고 응답을 받으면서 캐시 유효 시간이 60초인 이미지 파일을 같이 받아온다. 이 때, 서버의 파일 버전을 의미하는 Etag 헤더에 담긴 내용도 캐시에 함께 저장한다.
- 캐시 유효 시간인 60초를 초과한 후에 두 번째 요청을 보낸다고 해보자. 비록 유효 시간이 지났어도 해당 데이터를 재사용해도 되는지 확인하기 위해서 “내가 캐시에 저장해놓은 데이터 버전이랑 서버 데이터 버전이랑 일치하니? 일치한다면 캐시에 저장해놓은 데이터를 재사용해도 괜찮을까?”라는 뜻의 요청 헤더 I`f-None-Match` 를 작성하고 캐시에 함께 저장해놓았던 Etag 값을 담아 요청을 보낸다. 이 값을 이용해 서버 데이터의 Etag 와 캐시에 저장된 데이터의 Etag 를 비교한다. 두 데이터가 동일한 데이터라면 두 Etag 값이 같아야 한다.
304 Not Modified
라는 응답을 보내주고, 캐시 데이터의 유효 시간이 갱신되면서 해당 데이터를 재사용할 수 있게 된다.여기까지 두 쌍의 캐시 검증 헤더와 조건부 요청 헤더를 알아보았다. 두 쌍중의 헤더 중 한 쌍만 사용할 수도 있지만, 보통 두 종류를 동시에 사용한다. 둘 중 하나만 사용했다가 매칭되는 응답 헤더가 없는 경우에는 재사용할 수 있는 경우에도 리소스를 다시 받아와야 하는 경우가 생길 수 있기 때문이다.