티스토리 뷰
Spring MVC에서 Last-Modified와 If-Modified-Since 헤더를 통한 캐싱방법을 살펴보겠습니다. 웹 캐싱에 대한 이론적인 부분과 종류들은 다른 설명 글들이 많기 때문에 자세한 내용은 생략하도록 하겠습니다 (링크).
캐싱의 순서는 다음과 같이 이루어집니다.
브라우저에서 필요한 리소스를 서버에 최초로 요청합니다.
서버에서 응답헤더에
Last-Modified
헤더를 셋팅하여 요청한 리소스와 함께 내려보내면, 브라우저는 해당 리소스의 복사본을 생성하여 저장합니다.브라우저에서 해당 리소스를 재요청할 경우에는 서버에서
Last-Modified
헤더에 설정한 값을If-Modified-Since
헤더에 포함시켜 서버에 요청합니다.서버에서는
If-Modified-Since
헤더 값을 통해 해당 리소스가 변경되었으면Last-Modified
헤더에 최신 수정날짜를 설정하여 파일을 전달합니다. 변경되지 않았을 경우 304 HTTP 상태코드로 응답하면 브라우저는 기존에 저장한 값을 재사용하게 됩니다.
스프링에서는 ServletWebRequest
객체에 checkNotModified()
메서드를 구현하여 일련의 과정들을 큰 작업없이 가능하게 해줍니다.
해당 메서드는 리소스의 최근 수정일을 인자로 받아 브라우저에서 요청할 때 설정해주는 If-Modified-Since
헤더 값과 값을 비교하여 리소스가 변경이 없는 경우 true
를 반환하고 응답 값에 자동으로 304 NOT Modified를 설정해줍니다. 변경이 있는 경우에는 false
를 반환합니다.
boolean checkNotModified(long lastModifiedTimestamp);
브라우저에서 test.txt 파일을 요청한다고 가정해봅시다.
@GetMapping("/test")
public ResponseEntity<byte[]> test(ServletWebRequest request) throws IOException {
Resource resource = new ClassPathResource("test.txt");
// test.txt 파일의 최근 수정일을 인자값으로 넣어
// 변경이 일어났는지 여부를 확인합니다.
if (request.checkNotModified(resource.lastModified())) {
// 변경이 없는 경우(혹은 최초 요청인 경우)
// 빈 본문 값과 함께 304 상태코드가 반환됩니다.
return null;
}
return ResponseEntity.ok()
.cacheControl(CacheControl.noCache())
// Last-Modified 헤더에 파일의 최근 수정일을 넣어 응답합니다.
.lastModified(resource.lastModified())
.body(FileCopyUtils.copyToByteArray(resoure.getFile()));
}
이제 실제 브라우저에서의 동작이 어떻게 되는지 살펴보겠습니다.
브라우저가 최초 요청 시 서버의 응답을 살펴보면 아래 이미지 처럼 200 상태코드와 함께 Last-Modified
헤더에 해당 파일의 최근 수정일을 설정하여 내려보내지게 됩니다.
두 번째 요청 시 아래 이미지 처럼 전에 서버에서 Last-Modified
헤더에 응답해준 값을 If-Modified-Since
헤더에 설정하여 요청하게 됩니다.
그러면 서버에서는 아래 이미지 처럼 304 Not Modified
상태코드를 반환하며 해당 파일의 내용을 전달하지 않습니다. 브라우저는 로컬에 캐싱해둔 파일을 재사용하게 됩니다.