
클라이언트가 동일한 리소스(/star.jpg)를 2번 GET 요청했을 때, 캐시 관련 기술이 적용되지 않았다면, HTTP 프로토콜에 의해 서버는 두 번의 요청에 대해 모두 리소스를 전송하게 된다. 이 경우 한 번의 요청에서 HTTP 헤더 0.1MB와 본문(이미지) 1MB로 총 1.1MB의 응답 용량이 발생하며, 두 번의 요청으로 총 2.2MB의 네트워크 리소스가 소비된다. 하지만 캐시가 적용되었다면, 첫 번째 요청에서 서버는 리소스와 함께 Cache-Control: max-age=3600과 같은 헤더를 통해 캐시를 설정할 수 있다. 이후 클라이언트는 동일 리소스 요청 시 If-None-Match 헤더로 리소스 변경 여부를 확인하며, 서버는 변경이 없음을 알리는 304 Not Modified 응답을 전송한다. 이 경우, 첫 번째 요청에서 1.1MB가 전송되고 두 번째 요청은 헤더 0.1MB만 전송되어 총 응답 용량이 1.2MB로 감소하며, 약 45%의 네트워크 자원을 절약할 수 있다. HTTP 캐시는 이처럼 네트워크 자원 절약과 클라이언트 응답 속도 향상에 유용하다.
HTTP/1.1 200 OK
Content-Type: image/jpeg
cache-control: max-age=60
Content-Length: 34012
lkj123kljoiasudlkjaweioluywlnfdo912u34ljko98udjklaslkjdfl;qkawj9;o4ruawsldkal;skdjfa;ow9ejkl3123123
cache-control: max-age=60 : 캐시 유효기간을 60초로 설정한다. cache-control 헤더에 의해 브라우저 캐시에 응답 데이터가 60초 동안 존재하게 된다. 캐시가 60초 후 기간 만료가 되면, 서버는 다시 응답 데이터를 1.1MB로 송신하며, 새로운 60초에 해당하는 캐시 정보를 클라이언트에 전달한다.이를 통해 두 번째 요청에서는 캐시를 이용할 수 있다. 클라이언트는 동일한 요청을 서버에 보내고, 서버는 해당 요청을 확인하여 헤더 데이터(0.1MB)만 응답하며, 클라이언트는 캐시에서 본문 데이터를 조회한다.
캐시 방식을 사용하지 않을 경우 데이터가 변경되지 않아도 계속 네트워크를 통해 데이터를 다운로드받아야 한다. 이는 느린 사용자 경험을 초래할 수 있다. 반면 위와 같이 캐시를 효율적으로 사용하면 비싼 네트워크 사용량을 줄이고, 빠른 사용자 경험을 제공할 수 있다.
HTTP/1.1 200 OK
Content-Type: image/jpeg
cache-control: max-age=60
Last-Modified: 2020년 11월 10일 10:00:00
Content-Length: 34012
lkj123kljoiasudlkjaweioluywlnfdo912u34ljko98udjklasl
kjdfl;qkawj9;o4ruawsldkal;skdjfa;ow9ejkl3123123
cache-control 외에 Last-Modified가 추가된다. 이는 동일한 요청에 대해 서버 데이터의 수정 여부를 판단하는 역할을 한다. 브라우저 캐시에 저장되는 이미지 데이터는 max-age와 Last-Modified를 함께 가진다.
60초 이내에는 max-age를 기준으로 캐시에서 데이터를 가져온다. 하지만 60초 이후, 즉 캐시 기간이 만료된 상황에서는 Last-Modified가 고려된다. 이때 클라이언트는 캐시에서 저장된 Last-Modified 시간을 요청에 포함하여 서버로 보낸다. 서버에서 클라이언트의 Last-Modified 값과 해당 리소스의 최신 Last-Modified 값을 비교한다. 만약 두 값이 같다면, 서버는 변경이 없음을 나타내는 304 Not Modified 상태 코드와 함께 캐시를 재활용하도록 지시하며, 캐시 기간을 다시 갱신한다.(60초 다시 활성화)
GET /star.jpg
if-modified-since: 2020년 11월 10일 10:00:00
304 Not Modified발생시 위와 같이 캐시의 데이터에 Last-Modified가 존재할 경우 요청 헤더에 if-modified-since에 해당 시간을 담아 서버로 보낸다.
즉 정리하면 캐시의 유효 시간을 초과해도 서버의 데이터가 갱신되지 않으면 304 Not Modified로 하여금 클라이언트가 서버가 보낸 응답 헤더 정보로 캐시의 메타 정보를 갱신하고, 클라이언트는 캐시의 데이터를 재활용한다.
ETag란 캐시용 데이터에 임의의 고유한 버전 이름을 붙이는 것을 말하며, 주로 해시 알고리즘을 사용하여 생성된다. 요청에 대한 응답 데이터의 해시 알고리즘에 의해 생성된 고유한 번호를 ETag 헤더에 포함해 클라이언트 측으로 응답한다.
브라우저 캐시는 ETag 값과 함께 응답 데이터를 저장하며, 클라이언트는 재요청 시 저장된 ETag 값을 함께 서버로 보낸다. 서버는 클라이언트가 보낸 ETag와 현재 리소스의 해시 값(해시Func(리소스))을 비교한다. 만약 리소스가 수정되어 ETag 값이 변경되었다면, 서버는 새로운 응답 데이터를 구성해 클라이언트로 전송한다. 반면, ETag 값이 동일하다면 서버는 변경이 없음을 의미하는 304 Not Modified 응답을 반환하며 캐시를 재활용하도록 한다.
ETag를 이용하면 캐시 제어 로직을 서버에서 완전히 관리할 수 있다. Last-Modified 역시 서버에서 계산되지만, 단순히 수정 시간을 비교하는 방식으로 동작한다. 이와 달리 ETag는 서버에서 정의한 해시 알고리즘에 따라 동작하므로 더 세밀하고 유연한 캐시 제어가 가능하다.
max-age 사용이 권장된다.한국의 클라이언트가 미국의 원 서버까지 도달하는 시간은 꽤 걸리기 때문에, 실제로 클라이언트는 한국 어딘가에 존재하는 프록시 캐시 서버를 이용하는 경우가 많다. 프록시 캐시 서버는 public 캐시를 가지고 있으며, private 캐시는 브라우저와 같은 개별 클라이언트가 보유하고 있다.
Cache-Control: public -> 프록시 캐시 서버로
Cache-Control: private -> 해당 사용자만을 위함(브라우저 캐시)
Cache-Control: s-maxage -> 프록시 캐시에만 적용되는 max-age
확실한 캐시 무효화를 위해서는 다음과 같이 응답메시지 헤더를 구성해야한다.
• Cache-Control: no-cache, no-store, must-revalidate
• Pragma: no-cache
no-cache는 데이터를 캐시할 수 있지만 항상 원 서버에 검증하도록 한다. no-store는 데이터를 캐시에 저장하지 않도록 한다. must-revalidate는 캐시 만료 후 최초 조회 시 원 서버에 검증을 요구하며, 원 서버 접근 실패 시 반드시 504 Gateway Timeout 오류를 발생시키도록 한다.