Spring Web Reactive | 1. Spring WebFlux | 1.10. HTTP 캐싱

Web MVC

HTTP 캐싱은 Web 어플리케이션의 성능을 크게 개선시켜 준다. HTTP 캐싱은 Cache-Control 응답 헤더와 Last-ModifiedETag와 같이 이어지는 조건부 요청 헤더를 중심으로 동작한다. Cache-Control는 프라이빗 (브라우저 등) 및 퍼블릭(프록시 등)의 캐시가 응답을 캐시하여 재사용하는 방법을 권고한다. 컨텐츠가 변경되지 않은 경우, ETag 헤더를 사용하여 본문(body)이 없는 304 (NOT_MODIFIED)가 될 조건부 요청을 만든다. ETagLast-Modified 헤더보다 정교한 것으로 대체 버전으로 볼 수 있다.

이번 섹션에서는 Spring WebFlux에서 사용 가능한 HTTP 캐싱 관련 옵션에 대해 설명한다.

1.10.1. CacheControl

Web MVC

CacheControlCache-Control 헤더에 관련 설정 구성을 지원하고, 많은 곳에서 인수로 허용된다.

RFC 7234Cache-Control 응답 헤더의 가능한 모든 지시어(directive)를 기술한다. CacheControl 유형은 다음의 예와 같이 일반적인 시나리오에 초점을 맞춘 사례 중심의 접근 방식을 취한다.

Java

// Cache for an hour - "Cache-Control: max-age=3600"
CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS);

// Prevent caching - "Cache-Control: no-store"
CacheControl ccNoStore = CacheControl.noStore();

// Cache for ten days in public and private caches,
// public caches should not transform the response
// "Cache-Control: max-age=864000, public, no-transform"
CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic();

Kotlin

// Cache for an hour - "Cache-Control: max-age=3600"
val ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS)

// Prevent caching - "Cache-Control: no-store"
val ccNoStore = CacheControl.noStore()

// Cache for ten days in public and private caches,
// public caches should not transform the response
// "Cache-Control: max-age=864000, public, no-transform"
val ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic()

1.10.2. 컨트롤러

Web MVC

컨트롤러는 HTTP 캐싱의 명시적인 지원을 추가 할 수 있다. 리소스 lastModified 또는 ETag 값은 조건부 요청 헤더와 비교하기 전에 계산해야 하므로 이런 방식을 추천한다. 다음의 예와 같이, 컨트롤러에 ETag, Cache-Control 설정을 ResponseEntity에 추가 할 수 있다.

Java

@GetMapping("/book/{id}")
public ResponseEntity<Book> showBook(@PathVariable Long id) {

    Book book = findBook(id);
    String version = book.getVersion();

    return ResponseEntity
            .ok()
            .cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
            .eTag(version) // lastModified is also available
            .body(book);
}

Kotlin

@GetMapping("/book/{id}")
fun showBook(@PathVariable id: Long): ResponseEntity<Book> {

    val book = findBook(id)
    val version = book.getVersion()

    return ResponseEntity
            .ok()
            .cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
            .eTag(version) // lastModified is also available
            .body(book)
}

위의 예제에서는 조건부 요청 헤더에 비해 콘텐츠가 변경되지 않았다면, 빈 본문를 포함한 304 (NOT_MODIFIED) 응답이 전송된다. 그렇지 않으면, ETagCache-Control 헤더가 응답에 추가된다.

다음의 예와 같이 컨트롤러의 조건부 요청 헤더에 체크를 할 수도 있다.

Java

@RequestMapping
public String myHandleMethod(ServerWebExchange exchange, Model model) {

    long eTag = ...   // (1)

    if (exchange.checkNotModified(eTag)) {
        return null;   // (2)
    }

    model.addAttribute(...);   // (3)
    return "myViewName";
}

Kotlin

@RequestMapping
fun myHandleMethod(exchange: ServerWebExchange, model: Model): String? {

    val eTag: Long = ...   // (1)

    if (exchange.checkNotModified(eTag)) {
        return null  // (2)
    }

    model.addAttribute(...)   // (3)
    return "myViewName"
}
  • (1) 응용 프로그램의 고유 방식으로 계산한다.
  • (2) 응답은 304 (NOT_MODIFIED)로 설정한다. 더 이상 다른 처리는 하지 않는다.
  • (3) 요청 처리를 계속한다.

조건부 요청을 eTag 값, lastModified 값 또는 둘 모두에 대해 확인하기 위해 3개의 변형이 있다. 조건부 GETHEAD 요청인 경우, 응답을 304 (NOT_MODIFIED)에 설정할 수 있다. 조건부 POST, PUT, DELETE의 경우 대신 응답을 412 (PRECONDITION_FAILED)로 설정하여 동시 변경을 방지 할 수 있다.

1.10.3 정적 리소스

Web MVC

최적의 성능을 얻으려면, Cache-Control와 조건부 응답 헤더에서 정적 리소스를 제공해야 한다. 정적 리소스 구성 섹션을 참조해라.




최종 수정 : 2021-04-12