Backend

spring boot 에서 etag 설정하기

!쪼렙조햄 2021. 11. 29. 14:57
반응형

0. 배경

RFC(Request for Comments) 표준 방식에 의해 캐시 validation 방식이 변경됨
기존 : etag, last-modified 둘다 비교
변경 : etag 만 비교

etag 도입이 필요하다

1. etag 란

ETag == entity tag == 응답 body 값의 해시 값
HTTP 응답 헤더에 담아서 보내준다
응답 body 가 변하면 etag 도 변해야 한다

2. spring boot 에서 etag를 사용하기 위한 전제조건

# web.xml or build.gradle

<filter>
   <filter-name>etagFilter</filter-name>
   <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>etagFilter</filter-name>
   <url-pattern>/foos/*</url-pattern>
</filter-mapping>

3-1. (내가 사용한 방법) servlet filter 사용

servlet filter 란? 
Client -> Server 로 요청이 들어오기 전에 서블릿을 거쳐서 필터링 하는 것

@Bean
public ShallowEtagHeaderFilter shallowEtagHeaderFilter() {
    return new ShallowEtagHeaderFilter();
}

위 코드만 작성해주면 해당 스프링부트 앱에서 제공하는 모든 응답에 etag 가 붙게 된다

@Bean
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
    FilterRegistrationBean<ShallowEtagHeaderFilter> filterRegistrationBean
      = new FilterRegistrationBean<>( new ShallowEtagHeaderFilter());
    filterRegistrationBean.addUrlPatterns("/foos/*");
    return filterRegistrationBean;
}

나의 경우 위와 같이 UrlPattern을 등록해서, 
등록된 url 과 맞는 패턴의 경우에만 etag가 붙도록 했다

3-2. ResponseEntity 의 etag 메소드 사용하기

@GetMapping(value = "/{id}/custom-etag")
public ResponseEntity<Foo>
  findByIdWithCustomEtag(@PathVariable("id") final Long id) {

    // ...Foo foo = ...

    return ResponseEntity.ok()
      .eTag(Long.toString(foo.getVersion()))
      .body(foo);
}

위와 같이 ResponseEntity 에 .etag 메소드로 etag를 지정해줄 수 있다.
이 경우, etag 를 만들기 위한 해싱에 사용될 값을 직접 정의할 수 있고,
딱 etag 를 적용할 API 에만 적용할 수 있다는 장점이 있다

4. 의문점

의문점 1. ShallowEtagHeaderFilter 는 어떤 값을 받아서 해싱하는가?

답변 1. 응답 결과의 body 를 받아서 해싱하는게 아닐까?
spring web 의 ShallowEtagHeaderFilter.java 소스코드를 확인해보았지만 명확한 답을 찾지 못했음
(https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java#L95)

의문점 2. Servlet filter 을 사용하면 스프링의 모든 API 응답 헤더에 etag가 붙게 되는가?

이론상 다 붙어야 한다고 생각했는데, 적용해 보다보니 붙는 곳도 있고, 안붙는 곳도 있는 것 같다
* 테스트를 해보니 ResponseEntity 에 lastModified 와 cacheControl 설정 두개를 해주어야 자동으로 etag 가 헤더에 붙어 나왔다
* 아무래도 둘다 설정이 되어있어야 캐시 사용한다고 인식해서 그런듯..?
* 그런데 lastmodified 와 etag 는 양쪽다 validation 을 위해 사용되는데 왜 둘다 필요한지 의문..

5. 참고 링크

https://www.baeldung.com/etags-for-rest-with-spring

반응형