Spring

@ControllerAdvice와 RequestBodyAdvice을 이용한 RequestBody 데이터 변경 방법

Flambee 2021. 3. 20. 00:15

Spring Annotation @ControllerAdvice와 RequestBodyAdvice

이 글에 앞서, 일단 @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