Spring/Spring MVC

[Spring MVC] 7. HTTP Caching

noahkim_ 2025. 4. 14. 18:55
  • 웹 애플리케이션의 성능을 높이기 위한 방법
  • 브라우저나 프록시 서버가 응답을 저장하고 재사용할 수 있게 도와주는 매커니즘

 

1. Cache-Control

  • 클라이언트나 프록시에게 응답을 어떻게 캐시될 지 알리는 HTTP 헤더 (응답 헤더)

 

흐름

  1. 클라이언트가 자원을 요청
  2. 서버는 응답 시, Cache-Control 헤더를 포함해서 응답
  3. 클라이언트가 해당 설정에 따라 캐싱 자원으로 응답할 지 결정

 

예시) 뉴스 기사 요청하기

더보기
더보기
GET /news/123
HTTP/1.1 200 OK
Cache-Control: max-age=60
Content-Type: text/html
  • 브라우저에 응답을 캐싱함

 

GET /news/123
  • 60초 이내 재요청 시, 서버로 요청이 가지 않음. (캐시 사용)
  • 60초 이후 재요청 시, 서버로 새 요청 (캐시 만료)

 

Spring

  • WebMvcConfigurer에서 Interceptor or ResourceHandler를 설정할 수 있습니다.
  • ResonseEntity로 직접 응답할 수 있습니다.

 

설정) WebMvcConfigurer

더보기
더보기

핸들러에 적용

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        WebContentInterceptor interceptor = new WebContentInterceptor();
        interceptor.addCacheMapping(
            CacheControl.maxAge(3600, TimeUnit.SECONDS),
            "/api/public/**"
        );
        registry.addInterceptor(interceptor);
    }
}

 

정적 리소스에 적용

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/static/**")
            .addResourceLocations("classpath:/static/")
            .setCacheControl(CacheControl.maxAge(30, TimeUnit.DAYS));
}

 

 

예제) ResonseEntity로 응답하기

더보기
더보기
@GetMapping("/book/{id}")
public ResponseEntity<Book> showBook(@PathVariable Long id) {
    Book book = findBook(id);
    String version = book.getVersion(); // 예: "v1.2"
    
    // "Cache-Control: max-age=3600" (1시간 동안 캐시)
    CacheControl cc1 = CacheControl.maxAge(1, TimeUnit.HOURS); 

    // "Cache-Control: no-store" (캐시하지 않음)
    CacheControl cc2 = CacheControl.noStore();

    // "Cache-Control: max-age=864000, public, no-transform" (10일 동안 캐시, 공용 캐시 가능, 변형 금지)
    CacheControl cc3 = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic();

    return ResponseEntity
        .ok()
        .cacheControl(cc3) // 원하는 cache control 선택
        .eTag(version)
        .body(book);
}

 

 

2. Last-Modified

  • 리소스가 마지막으로 변경된 날짜와 시간을 나타내는 HTTP 헤더 (요청 헤더)
  • 불필요한 응답을 줄여줍니다

 

흐름

  1. 클라이언트가 자원을 요청합니다.
  2. 서버는 리소스의 가장 최신에 있었던 수정 날짜를 Last-Modified 헤더로 응답합니다.
  3. 클라이언트는 다음 요청 시, 응답받은 Last-Modified 헤더값을 If-Modified-Since 헤더에 포함시켜 요청합니다.
  4. 서버는 이 값과 리소스의 최근 수정 시각을 비교하고, 변경되지 않았다면 304 응답 코드를 내려줍니다.

 

예제) 자원 요청하기

더보기
더보기
GET /resource
200 OK
Last-Modified: Fri, 12 Apr 2024 12:00:00 GMT

 

GET /resource
If-Modified-Since: Fri, 12 Apr 2024 12:00:00 GMT
304 Not Modified

 

Spring

  • WebRequest, HttpServletRequest는 If-Modified-Since 헤더를 접근 기능을 제공합니다.

 

예제) HttpServletRequest

더보기
더보기
@GetMapping("/api/book/{id}")
public ResponseEntity<Book> getBook(@PathVariable Long id, WebRequest request) {
    Book book = findBook(id);
    long lastModified = book.getUpdatedAt().toEpochMilli();

    if (request.checkNotModified(lastModified)) {
        return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build();
    }

    return ResponseEntity
            .ok()
            .lastModified(lastModified)
            .body(book);
}

 

3. ETag

ShallowEtagHeaderFilter

  • 응답 내용을 기반으로 자동으로 ETag를 생성해주는 필터입니다.

 

설정) WebInitliaizer#onStartup

더보기
더보기
FilterRegistration.Dynamic ETagHeaderFilter = servletContext.addFilter("eTagHeaderFilter", new ShallowEtagHeaderFilter());
ETagHeaderFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");

 


출처

 

'Spring > Spring MVC' 카테고리의 다른 글

[Tomcat] 1. Introduction  (0) 2025.04.14
[Spring MVC] 6. Error Response  (0) 2025.04.12
[Spring MVC] 3. HTTP Message Conversion  (0) 2025.04.12
[Spring MVC] 2. Filters  (0) 2025.04.11
[Spring MVC] 4-3. Handler Methods: Controller Advice  (0) 2023.10.17