[Spring] HTTP Method Conversion과 ETag(Entity Tag)란? X-HTTP-Method-Override, ShallowEtagHeaderFilter
by 우와한개발자
[HTTP Method Conversion]
1. HTTP Method Conversion이란?
- 일부 방화벽, 프록시, 오래된 브라우저는 GET, POST만 허용하고 PUT, PATCH, DELETE 요청이 차단되는 경우 발생하는데 이때, POST 요청으로 PUT, PATCH, DELETE를 흉내낼 수 있게 해주는 기술
- POST 요청에 X-HTTP-Method-Override 헤더를 추가해 실제 메서드를 명시
- HttpFilter를 이용해 구현
클라이언트
POST /v1/users/123
X-HTTP-Method-Override: DELETE
↓
[HttpFilter에서 감지 후 변환]
↓
서버는 DELETE /v1/users/123 으로 처리
| 구분 | Filter | HttpFilter |
| 도입 | Servlet 2.3 | Servlet 4.0+ |
| 파라미터 타입 | ServletRequest (타입 캐스팅 필요) | HttpServletRequest (바로 사용 가능) |
| 사용 편의성 | 매번 캐스팅 필요 | HTTP 특화 메서드 바로 사용 가능 |
2. 구현 : HttpMethodOverrideFilter.java
@Component
public class HttpMethodOverrideFilter extends HttpFilter {
private static final String HEADER_NAME = "X-HTTP-Method-Override";
private static final Set<String> ALLOWED_OVERRIDE =
Set.of("PUT", "PATCH", "DELETE");
@Override
protected void doFilter(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain)
throws IOException, ServletException {
String overrideMethod = request.getHeader(HEADER_NAME);
// POST 요청이고, 유효한 Override 헤더가 있을 때만 변환
if ("POST".equalsIgnoreCase(request.getMethod())
&& overrideMethod != null
&& ALLOWED_OVERRIDE.contains(overrideMethod.toUpperCase())) {
chain.doFilter(
new MethodOverrideRequestWrapper(request, overrideMethod), response
);
} else {
chain.doFilter(request, response);
}
}
// HttpServletRequestWrapper로 메서드만 교체
private static class MethodOverrideRequestWrapper extends HttpServletRequestWrapper {
private final String overrideMethod;
public MethodOverrideRequestWrapper(HttpServletRequest request, String overrideMethod) {
super(request);
this.overrideMethod = overrideMethod.toUpperCase();
}
@Override
public String getMethod() {
return overrideMethod; // POST 대신 PUT/PATCH/DELETE 반환
}
}
}
[ETag(Entity Tag)
1. ETag(Entity Tag)란?
- HTTP 프로토콜에서 사용되는 웹 캐싱(웹 리소스의 변경 여부 확인) 및 동시성 제어를 위한 식별자
- 서버는 특정 리소스의 버전을 식별하기 위해 ETag 사용
- 클라이언트는 ETag를 저장해두었다가 다음 요청 시 서버에 전달해 변경 여부 확인
- 생성 방식 : 응답 본문 전체의 해시값 (파일 해시, 수정 시각, 버전 식별자 등)
⚠️ 단순 hashCode()나 id값은 충돌 위험 및 변경 감지 불가해 사용 비추천
2. ETag 동작 원리
- 서버에서 리소스를 반환할 때, HTTP 응답 헤더에 포함
- 보통 파일의 해시값, 마지막 수정시각, 고유한 버전 식별자 등으로 생성됨
[최초 요청]
클라이언트 → GET /v1/users/1
서버 ← 200 OK
ETag: "abc123"
body: { id:1, name:"홍길동" }
[재요청 - 변경 없음]
클라이언트 → GET /v1/users/1
If-None-Match: "abc123"
서버 ← 304 Not Modified (body 없음, 네트워크 절약)
[재요청 - 변경 있음]
클라이언트 → GET /v1/users/1
If-None-Match: "abc123"
서버 ← 200 OK
ETag: "xyz789" (새 ETag)
body: { id:1, name:"김철수" } (변경된 데이터)
3. Spring Boot에서 ETag 구현
- Spring Boot는 ShallowEtagHeaderFilter를 통해 ETag를 자동으로 생성 및 비교
1) ShallowEtagHeaderFilter (전역 적용)
- 모든 응답에 ETag를 자동으로 생성 및 비교
- 응답 body 전체를 MD5로 해싱해서 ETag 생성
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
Filter shallowEtagHeaderFilter() {
return new ShallowEtagHeaderFilter(); // 전역으로 모든 응답에 ETag 적용
}
}
2) ResponseEntity.eTag() (컨트롤러 개별 적용)
- 특정 핸들러 메서드에만 ETag를 적용하고 싶을 때
- 개발자가 직접 ETag 값을 지정
(1) Entity에 @Version 추가
@Entity
@Table(name = "users")
@Getter @Setter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private int age;
@Version // 변경될 때마다 자동으로 1씩 증가
private Long version;
}
(2) Controller
@GetMapping("/{id}")
public ResponseEntity<UserDto> getUserById(@PathVariable Long id) {
Optional<UserDto> user = userService.getUserById(id);
if (user.isPresent()) {
// hashCode() 대신 version 필드 사용
String eTag = "\\"" + user.get().getVersion() + "\\"";
return ResponseEntity
.ok()
.eTag(eTag)
.body(user.get());
} else {
return ResponseEntity.notFound().build(); // 404 Not Found
}
}
@Version 필드는 JPA가 UPDATE 시마다 **자동으로 1씩 증가**시켜줌
→ 리소스 변경 여부를 정확하게 감지 가능
→ hashCode()처럼 충돌 위험 없음
| 구분 | ShallowEtagHeaderFilter | ResponseEntity.eTag() |
| 적용 범위 | 전역 (모든 응답) | 특정 메서드만 |
| ETag 생성 | body 전체 MD5 해싱 (자동) | 개발자가 직접 지정 |
| 설정 위치 | Config 클래스 | Controller 메서드 |
| 구현 난이도 | 쉬움 | 직접 관리 필요 |
| 세밀한 제어 | 불가 | 가능 |
'프레임워크 > Spring' 카테고리의 다른 글
| [Spring] WebSocket이란? 스프링 프로젝트 웹소켓 구현 (0) | 2026.04.07 |
|---|---|
| [Spring] Spring HATEOAS란? – EntityModel, ModelAssembler 구현 (0) | 2026.04.07 |
| [Spring] REST API 구현 (Spring Boot, JPA, H2)과 Content Negotiation(콘텐츠 협상) (0) | 2026.04.07 |
| [ Spring ] REST API란? – 설계 원칙, HTTP 메서드, 상태코드 정리 (0) | 2026.04.07 |
| [JWT] Spring Security JWT 인증 구현 –JwtTokenProvider, JwtAuthenticationFilter (0) | 2026.04.06 |
블로그의 정보
우와한개발자 님의 블로그
우와한개발자