잡동사니를 모아두는 서랍장
Request Body를 따로 읽고 싶을 때 본문
Request Body를 인터셉터에서 읽고 싶을 때 2편 주의
꼭 인터셉터가 아니라도 body 값은 한번 읽히면 날아간다는 건 동일하다. 저번엔 필터에서 래핑하여 인터셉터에서 읽었지만 그렇게 하면 성능 이슈가 있다고 한다.
이번엔 다른 대안으로 소개받은 RequestBodyAdviceAdapter 를 사용해서 body값을 다른 곳에서 읽어보려 한다. 정확히 말하자면 body를 읽는건 여전히 한번이지만, 이 내용이 변환된 객체를 컨트롤러로 넘어가기 전에 확인하는 작업을 해보자.
RequestBodyAdviceAdapter 는 RequestBodyAdvice 인터페이스를 구현한 클래스이다. RequestBodyAdvice 는
Body의 내용을 읽고 Object로 변환하기 전에 요청을 커스텀 하거나, 변환된 Object가 컨트롤러 메소드에서 @RequestBody 에 매핑될 때 이 Object로 따로 작업을 처리를 할 수 있다.
@ControllerAdvice 로 어드바이스를 명시해줘야 하며, RequestBodyAdviceAdapter 의 supports 메소드의 리턴값이 true 일 경우만 어드바이스가 적용된다(API 문서 제대로 안읽어서 이거 모르고 30분을 삽질했다...). 이 supports를 잘 이용하면 특정 상황에서만 어드바이스를 적용시킬 수도 있을거다.
아래는 예시 코드다((저세상 네이밍과 코드상태는 무시하자).
먼저 RequestBodyAdviceAdapter 를 상속하여 새로운 어드바이스 어댑터를 만들었다. supports 를 무조건 true로 리턴시켜놔서 모든 컨트롤러로 요청에 대해 매핑이 이루어질 때 어드바이스가 적용된다. 이제 요청이 들어와 body가 읽혀 변환이 일어나기 전후로 어드바이스가 적용되는 것을 확인할 수 있다.
여기서는 어차피 자바 객체로 매핑된 내용을 확인하는 것이 목표라 body 값이 읽혀 변환된 후에 적용되는 afterBodyRead 만 내용이 찍히게 로그를 남기고 읽히기 전에 적용되는 beforeBodyRead 는 단순히 실행됬다는 표시만 남게 로그를 찍었다. 로그는 귀찮아서 그냥 콘솔로 찍었다...
@ControllerAdvice(annotations = RestController.class)
public class CustomRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {
@Override
public Object afterBodyRead(final Object body, final HttpInputMessage inputMessage,
final MethodParameter parameter,
final Type targetType, final Class<? extends HttpMessageConverter<?>> converterType) {
System.out.printf("afterBodyRead ");
if (body instanceof ReqBody) {
ReqBody reqBody = (ReqBody) body;
System.out.printf("dong = %s, ho = %s\n", reqBody.getDong(), reqBody.getHo());
}
return body;
}
@Override
public HttpInputMessage beforeBodyRead(final HttpInputMessage inputMessage,
final MethodParameter parameter,
final Type targetType, final Class<? extends HttpMessageConverter<?>> converterType)
throws IOException {
System.out.println("beforeBodyRead");
return inputMessage;
}
@Override
public Object handleEmptyBody(final Object body, final HttpInputMessage inputMessage,
final MethodParameter parameter, final Type targetType,
final Class<? extends HttpMessageConverter<?>> converterType) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean supports(final MethodParameter methodParameter, final Type targetType,
final Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
}
컨트롤러. 어드바이스가 적용되는 시점을 확인하기 위해 여기도 콘솔로 찍었다.
@RestController
public class ControllerBodyAdvice {
@PostMapping(value = "/body/advice")
public void postBodyAdvice(@RequestBody @Valid final ReqBody reqBody) {
System.out.printf("controller dong = %s, ho = %s\n", reqBody.getDong(), reqBody.getHo());
}
}
바디 객체
@Data
public class ReqBody {
private String dong;
private String ho;
}
결과
beforeBodyRead
afterBodyRead dong = 101, ho = 1001
controller dong = 101, ho = 1001
보다시피 Body 값이 읽힌 후 컨트롤러로 넘어가기 전에 값을 확인할 수 있고 어드바이스 적용 후 컨트롤러에서도 멀쩡하게 값을 확인할 수 있다!!
공부할 땐 시간 엄청 걸렸는데 막상 적어보니 내용이 허접하네;; API 문서나 레퍼런스 읽을 때마다 시간이 너무 걸린다. 번역기로 돌리면 아무래도 이해 안되게 바뀌는 부분이 있어서 어느정도는 돌리고 나머진 해석하려 하는데 잘될리가 없다. 영어 공부의 필요성을 느끼지만 너무 귀찮다...
'Spring' 카테고리의 다른 글
@Transactional 이 안먹힐 때 참고할 기본적인것들 (0) | 2021.01.29 |
---|---|
Request Body를 인터셉터에서 읽고 싶을 때 (0) | 2020.04.20 |
스프링의 간단한 Async 처리 (0) | 2020.02.27 |
인터셉터와 필터 차이 (0) | 2020.02.23 |
PropertySourcesPlaceholderConfigurer로 프로퍼티로 지정하기 (0) | 2020.02.22 |