1. 개요
이 사용방법(예제)에서는 HTTP 캐싱에 대해 알아 봅니다. 또한 클라이언트와 Spring MVC 애플리케이션간에이 메커니즘을 구현하는 다양한 방법을 살펴볼 것입니다.
2. HTTP 캐싱 소개
브라우저에서 웹 페이지를 열면 일반적으로 웹 서버에서 많은 리소스를 다운로드합니다.
예를 들어,이 예에서 브라우저는 하나의 / login 페이지에 대해 세 개의 리소스를 다운로드해야 합니다. 브라우저가 모든 웹 페이지에 대해 여러 HTTP 요청을 만드는 것이 일반적입니다. 이제 이러한 페이지를 매우 자주 요청하면 네트워크 트래픽이 많이 발생하고 이러한 페이지를 제공하는 데 시간이 더 오래 걸립니다 .
네트워크 부하를 줄이기 위해 HTTP 프로토콜을 사용하면 브라우저가 이러한 리소스 중 일부 를 캐시 할 수 있습니다. 활성화 된 경우 브라우저는 로컬 캐시에 리소스 사본을 저장할 수 있습니다. 결과적으로 브라우저는 네트워크를 통해 요청하는 대신 로컬 저장소에서 이러한 페이지를 제공 할 수 있습니다.
웹 서버는 응답에 Cache-Control 헤더를 추가하여 특정 리소스를 캐시하도록 브라우저에 지시 할 수 있습니다 .
리소스가 로컬 사본으로 캐시되기 때문에 브라우저에서 오래된 콘텐츠를 제공 할 위험이 있습니다. 따라서 웹 서버는 일반적으로 Cache-Control 헤더 에 만료 시간을 추가합니다 .
다음 섹션에서는 Spring MVC 컨트롤러의 응답에이 헤더를 추가합니다. 나중에 만료 시간을 기준으로 캐시 된 리소스의 유효성을 검사하는 Spring API도 볼 수 있습니다.
3. 컨트롤러 응답의 캐시 제어
3.1. ResponseEntity 사용
이를 수행하는 가장 간단한 방법은 Spring에서 제공 하는 CacheControl 빌더 클래스 를 사용하는 것입니다 .
@GetMapping("/hello/{name}")
@ResponseBody
public ResponseEntity<String> hello(@PathVariable String name) {
CacheControl cacheControl = CacheControl.maxAge(60, TimeUnit.SECONDS)
.noTransform()
.mustRevalidate();
return ResponseEntity.ok()
.cacheControl(cacheControl)
.body("Hello " + name);
}
그러면 응답에 Cache-Control 헤더 가 추가됩니다 .
@Test
void whenHome_thenReturnCacheHeader() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.get("/hello/baeldung"))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.header()
.string("Cache-Control","max-age=60, must-revalidate, no-transform"));
}
3.2. HttpServletResponse 사용
종종 컨트롤러는 핸들러 메서드에서 뷰 이름을 반환해야합니다. 그러나 ResponseEntity 클래스는 뷰 이름을 반환하고 동시에 요청 본문을 처리하는 것을 허용하지 않습니다 .
또는 이러한 컨트롤러의 경우 HttpServletResponse에서 직접 Cache-Control 헤더를 설정할 수 있습니다.
@GetMapping(value = "/home/{name}")
public String home(@PathVariable String name, final HttpServletResponse response) {
response.addHeader("Cache-Control", "max-age=60, must-revalidate, no-transform");
return "home";
}
이것은 또한 마지막 섹션과 유사한 HTTP 응답에 Cache-Control 헤더를 추가합니다 .
@Test
void whenHome_thenReturnCacheHeader() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.get("/home/baeldung"))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.header()
.string("Cache-Control","max-age=60, must-revalidate, no-transform"))
.andExpect(MockMvcResultMatchers.view().name("home"));
}
4. 정적 리소스에 대한 캐시 제어
일반적으로 Spring MVC 애플리케이션 은 HTML, CSS 및 JS 파일과 같은 많은 정적 리소스를 제공 합니다. 이러한 파일은 많은 네트워크 대역폭을 사용하므로 브라우저가 파일을 캐시하는 것이 중요합니다. 응답 의 Cache-Control 헤더를 사용하여 다시 활성화합니다 .
Spring을 사용하면 리소스 매핑에서이 캐싱 동작을 제어 할 수 있습니다.
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/")
.setCacheControl(CacheControl.maxAge(60, TimeUnit.SECONDS)
.noTransform()
.mustRevalidate());
}
이렇게하면 / resources 아래에 정의 된 모든 리소스 가 응답 의 Cache-Control 헤더 와 함께 반환됩니다 .
5. 인터셉터의 캐시 제어
Spring MVC 애플리케이션에서 인터셉터를 사용 하여 모든 요청에 대해 사전 및 사후 처리를 수행 할 수 있습니다. 이것은 애플리케이션의 캐싱 동작을 제어 할 수있는 또 다른 자리 표시 자입니다.
이제 사용자 정의 인터셉터를 구현하는 대신 Spring에서 제공 하는 WebContentInterceptor를 사용합니다 .
@Override
public void addInterceptors(InterceptorRegistry registry) {
WebContentInterceptor interceptor = new WebContentInterceptor();
interceptor.addCacheMapping(CacheControl.maxAge(60, TimeUnit.SECONDS)
.noTransform()
.mustRevalidate(), "/login/*");
registry.addInterceptor(interceptor);
}
여기에서 WebContentInterceptor를 등록 하고 마지막 몇 섹션과 유사한 Cache-Control 헤더를 추가했습니다 . 특히 다른 URL 패턴에 대해 다른 Cache-Control 헤더를 추가 할 수 있습니다 .
위의 예에서 / login으로 시작하는 모든 요청에 대해 다음 헤더를 추가합니다.
@Test
void whenInterceptor_thenReturnCacheHeader() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.get("/login/baeldung"))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.header()
.string("Cache-Control","max-age=60, must-revalidate, no-transform"));
}
6. Spring MVC에서 캐시 유효성 검사
지금까지 응답에 Cache-Control 헤더 를 포함하는 다양한 방법에 대해 설명했습니다 . 이는 max-age 와 같은 구성 속성을 기반으로 리소스를 캐시 할 클라이언트 또는 브라우저를 나타냅니다 .
일반적으로 각 리소스에 캐시 만료 시간을 추가하는 것이 좋습니다 . 결과적으로 브라우저는 캐시에서 만료 된 리소스를 제공하지 않을 수 있습니다.
브라우저는 항상 만료 여부를 확인해야하지만 매번 리소스를 다시 가져올 필요는 없습니다. 브라우저가 서버에서 리소스가 변경되지 않았 음을 확인할 수있는 경우 캐시 된 버전을 계속 제공 할 수 있습니다. 이를 위해 HTTP는 두 가지 응답 헤더를 제공합니다.
- Etag – 서버에서 캐시 된 리소스가 변경되었는지 여부를 확인하기 위해 고유 한 해시 값을 저장하는 HTTP 응답 헤더 – 해당 If-None-Match 요청 헤더는 마지막 Etag 값을 전달해야합니다.
- LastModified – 리소스가 마지막으로 업데이트 된 시간 단위를 저장하는 HTTP 응답 헤더 – 해당 If-Unmodified-Since 요청 헤더는 마지막 수정 날짜를 포함해야합니다.
이러한 헤더 중 하나를 사용하여 만료 된 리소스를 다시 가져와야하는지 확인할 수 있습니다. 헤더의 유효성을 검사 한 후 서버는 리소스를 다시 보내거나 변경이 없음을 나타내는 304 HTTP 코드를 보낼 수 있습니다 . 후자의 경우 브라우저는 캐시 된 리소스를 계속 사용할 수 있습니다.
는 LASTMODIFIED 헤더는 초 정밀도로 시간 간격을 저장할 수 있습니다. 이는 더 짧은 만료가 필요한 경우 제한 사항이 될 수 있습니다. 따라서 대신 Etag 를 사용하는 것이 좋습니다 . Etag 헤더는 해시 값을 저장 하므로 나노초와 같이 더 미세한 간격까지 고유 한 해시를 생성 할 수 있습니다.
즉, LastModified 를 사용하는 것이 어떤 모습인지 확인해 보겠습니다 .
Spring은 요청에 만료 헤더가 있는지 여부를 확인하는 몇 가지 유틸리티 메서드를 제공합니다.
@GetMapping(value = "/productInfo/{name}")
public ResponseEntity<String> validate(@PathVariable String name, WebRequest request) {
ZoneId zoneId = ZoneId.of("GMT");
long lastModifiedTimestamp = LocalDateTime.of(2020, 02, 4, 19, 57, 45)
.atZone(zoneId).toInstant().toEpochMilli();
if (request.checkNotModified(lastModifiedTimestamp)) {
return ResponseEntity.status(304).build();
}
return ResponseEntity.ok().body("Hello " + name);
}
Spring은 마지막 요청 이후 자원이 수정되었는지 확인하기 위해 checkNotModified () 메소드를 제공합니다 .
@Test
void whenValidate_thenReturnCacheHeader() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.add(IF_UNMODIFIED_SINCE, "Tue, 04 Feb 2020 19:57:25 GMT");
this.mockMvc.perform(MockMvcRequestBuilders.get("/productInfo/baeldung").headers(headers))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().is(304));
}
7. 결론
이 기사에서는 Spring MVC에서 Cache-Control 응답 헤더를 사용하여 HTTP 캐싱에 대해 배웠습니다 . ResponseEntity 클래스를 사용하거나 정적 리소스에 대한 리소스 매핑을 통해 컨트롤러의 응답에 헤더를 추가 할 수 있습니다 .
Spring 인터셉터를 사용하여 특정 URL 패턴에 대해이 헤더를 추가 할 수도 있습니다.
- https://docs.spring.io/spring-framework/docs/current/reference/html
- https://www.baeldung.com/spring-mvc-cache-headers
'Java' 카테고리의 다른 글
SpringMVC의 양식 태그 라이브러리 탐색 (0) | 2021.03.29 |
---|---|
자바 스크립트에서 Spring MVC 모델 객체에 접근하기 (0) | 2021.03.29 |
Spring MultipartFile을 파일로 변환 (0) | 2021.03.28 |
Spring @PathVariable 어노테이션 (0) | 2021.03.28 |
java.lang.Process API 사용방법(예제) (0) | 2021.03.28 |