이 글에 앞서, 일단 @ControllerAdvice에 대해서 짧게 알아가려고 합니다.
@ControllerAdvice를 아시나요?
@ControllerAdvice는 짧게 말해서 Spring이 제공하는 AOP(Aspect oriented programming)의 기능 중에 하나이며, 전역에 있는 컨트롤러에 공통적으로 사용되는 것이 있을때, 적용시켜주는 annotation입니다.
현재 흔히 사용하는 @ControllerAdvice의 방법은 대표적으로 Global ExceptionHandler를 만드는 방식으로 쓰이고 있고, 많은 블로그에서 대부분 @ControllerAdvice의 예제는 Global ExceptionHandler에 대한 예제로 가득차 있다. 또한 @ControllerAdvice는 Exception 처리만 도와주는 annotation이라고 많은 블로그에서 소개되고 있다.
찾아본 결과, 다른 해외 포럼에서는 @ControllerAdvice는 Global ExceptionHandler 방식이 아닌 다른 방식으로도 쓰이고 있고, Exception만 도와준다는 것이 아니였고, 아래와 같은 기능을 가지고 있었다.
@ControllerAdvice 대표적 기능
1. @ModelAttribute
2. @ExceptionHandler
3. @InitBinder
4. RequestBodyAdvice, ResponseBodyAdvice
대표적인 기능은 궁금하면 찾아보시길 바랍니다.
이정도면 @ControllerAdvice에 대해서 짧게 알아본 것 같으니..
이제 본론으로 들어가서, 오늘의 주제인 RequestBodyAdvice에 대해서 소개하겠습니다.
예를들어, 모든 컨트롤러나, 특정 다수의 컨트롤러에 암호화 된 데이터가 @RequestBody형태로 들어오고 있고, 해당 암호화된 데이터는 항상 똑같은 DTO(Data Transfer Object)를 통하여 받아, 암호화된 데이터를 복호화시켜서 데이터를 써야 된다고 생각을 해봅시다.
class EncDTO {
String enc;
String dec;
public String getDec() {
this.dec = decfunction(dec);
}
...
}
여기서 getDec를 한번만 쓰면 괜찮겠지만, 계속 쓰게 된다면 decfunction은 계속 호출이 될 것이라고 생각합니다.
또한. 해당 getDec를 쓰지 않고, decfunction을 해당 @Controller, @Service, @Repository에 쓰면, boilerplate가 발생하지 할겁니다.
여기서 생각한게 바로 API 요청을 하여 Controller에 들어가기 이전에 어떻게든 dec에 복호화된 데이터를 처음부터 셋팅을 해주면 좀 더 깔끔하게 코드를 짤 수 있을 것 같아서, 알아보게 된 것이 바로. RequestBodyAdvice입니다.
@ControllerAdvice
public class CustomRequestBodyAdvice implements RequestBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return null;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
return null;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
return body;
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
return null;
}
}
RequestBodyAdvice는 @ControllerAdvice에서 사용되며, 인터페이스 이므로 implements를 사용해야하며, 크게 4가지 구성으로 @Override가 발생하며, 구성은 아래와 같습니다.
구성
- supports
- beforeBodyRead
- afterBodyRead
- handleEmptyBody
Supports :
- @ControllerAdvice 범위내에서 Controller가 실행이 됐을때 Controller에 대해 Check하는 곳, Boolean이며, True면 다음 단계로 넘어가고, false면 요청이 Controller단으로 넘어가지 않습니다.
- 또한 Supports는 RequestBodyAdvice 처음과 끝에서 실행되요
BeforeBodyRead
- Spring MVC가 RequestBody를 읽기전에 실행되는 Method 입니다.
afterBodyRead
- Spring MVC가 RequestBody를 읽은 후 실행되는 Method
- 여기서 Object를 가지고오는데, Object가 바로 Controller단에 @RequestBody annotation의 class object를 말합니다.
handleEmptyBody
- RequestBody가 비어져 있을 때 임의로 넣어줄 수 있는 곳입니다.
이렇게 구성되어 있으며, 앞서 Dec에 대한 function을 afterBodyRead에서 복호화해서 Controller단에다가 넘겨주면, 예에 대한 상황은 끝나는 것을 볼 수 있습니다.
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
EncDTO encDTO = (EncDTO)body; // EncDTO로 casting
encDTO.setDec(decfuntion(encDTO.getEnc)); // Enc값 복호화
return encDTO; // body대신 EncDTO를 넘겨줌.
}
이게 바로 RequestBodyAdvice를 쓸 수 있는 가장 간단한 상황이 아닐까하며, 좀 더 심화적이게 간다면, 컨트롤러에서 공통적인 데이터에 대한 체크가 필요하고, 이 데이터가 만약에 @Cacheable Annotation이 사용되어 캐시에 저장되어 있다면 저장되어 있는 데이터를 사용하고, 아니면 다시 불러와 캐시화 하는 로직에 대한 처리가 필요할 때 해당 방법을 사용하면 좋을 듯합니다.
AOP에 대한 구현은 코드상으로 복잡해지기도 하고, RequestBody에 대한 Stream은 AOP나 Filter에서 한번 꺼내면 없어지기 때문에, 자칫 잘못하다가는 더 복잡한 로직을 만들 수 있기 때문입니다.
이상 RequestBodyAdvice에 대한 설명이였습니다!
전 이만 요가하러 가볼게요~
'Spring' 카테고리의 다른 글
SpringBoot LogBack 간단설정 (0) | 2021.06.07 |
---|---|
SpringBoot Dynamic Log (0) | 2021.06.07 |
SpringBoot Kotlin Log(AOP/Filter) (0) | 2021.06.06 |
@Transactional 사용 시 주의해야하는 8가지 (0) | 2021.03.21 |
@Transactional을 아시나요? (0) | 2021.03.21 |