맨땅에 헤딩하는 개바른자

Controller Response 공통 처리 하기 본문

JAVA

Controller Response 공통 처리 하기

앵낄낄 2022. 4. 27. 21:20
반응형

프로젝트 구성 할 때 Response를 공통화하기위해 공통객체를 생성하고 Controller에서 공통객체로 리턴하는 구간을 간략하게 처리할 수 있는 방법입니다.

보통 Controller에서 공통처리 시 아래와 같이 선언이 됩니다.

최종 객체로 ResponseEntity를 선언하고 그 안에 공통 객체를 VgnApiResponse 사용하는 구성으로 코딩이 되어 있습니다.

@GetMapping("/me")
public ResponseEntity<VgnApiResponse<MemberDto>> getMyMemberInfo() {
		return ResponseEntity.ok(
            VgnApiResponse.<MemberDto>builder()
                    .logKey(LogKey.get())
                    .data(memberService.getMyInfo())
                    .build()
    );    
}

리턴형태

{
		"logKey": "5efb26f7-57a7-4767-919b-e5682ca9dfb0",
		"data": {
		    "id": 1,
		    "email": "test@hanatour.com",
		    "name": "테스트",
		    "nickname": "테스터",
		    "profileUrl": ""
}

아래코드를 보면 간결하고 명확한 리턴 값의 가독성을 높인 소스코드입니다.

@GetMapping("/me")
public MemberDto getMyMemberInfo() {
    return memberService.getMyInfo();
}

ResponseEntity, VgnApiResponse 객체가 사라진걸 보실 수 있습니다.

리턴형태도 이전과 동일한 것을 확인 할 수 있습니다.

{
    "logKey": "f6a2095b-f5f5-4218-8dcc-75e863c0fdab",
    "data": {
        "id": 1,
        "email": "test@hanatour.com",
        "name": "테스트",
        "nickname": "테스터",
        "profileUrl": ""
    }
}

그러면 위와 같이 간결하게하기 위해서 처리하는 과정을 알아보겠습니다.

우선 기존 사용했던 공통응답 객체는 아래와 같이 수정하였습니다.

static으로 of로 리턴 항목을 추가하는 메소드를 제거하고 생성자로 data를 주입받는 것으로 수정하였습니다.

[공통 Response 객체 생성]

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public final class VgnApiResponse<T> {

    private String logKey;
    private T data;

    public VgnApiResponse(T data) {
        this.logKey = LogKey.get();
        this.data = data;
    }
}

그리고 모든 Controller에서 응답될 때 걸쳐가는 구간에서 핸들링이 가능한 관련항목을 설정하여 자동으로 공통응답이 셋팅되도록 하였습니다.

[Response 핸들링 관련 설정]

@RestControllerAdvice
public class VgnResponseBodyAdvice implements ResponseBodyAdvice {

    @Override
    public boolean supports(final MethodParameter returnType, final Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return new ApiResponse(body);
    }
}

여기서 사용되는 기능 중 살펴볼 것을 2가지 입니다.

1. @RestControllerAdvice

@ControllerAdvice와 @ResponseBody를 합쳐놓은 어노테이션이다. @ControllerAdvice와 동일한 역할을 수행하고, 추가적으로 @ResponseBody를 통해 객체를 리턴할 수도 있다. 따라서 단순히 예외만 처리하고 싶다면 @ControllerAdvice를 적용하면 되고, 응답으로 객체를 리턴해야 한다면 @RestControllerAdvice를 적용하면 된다. 위 두 어노테이션 모두 적용 범위를 클래스나 패키지 단위로 제한할 수도 있으며, 아래와 같이 사용하면 된다

@RestControllerAdvice(basePackageClasses = Some.class) @RestControllerAdvice(basePackages = "com.package.path")

2. ResponseBodyAdvice 인터페이스
인터페이스를 들어가면 영문으로.. 아래 처럼 번역하면 글이 나온다.

@ResponseBody 또는 ResponseEntity 컨트롤러 메서드를 실행한 후 HttpMessageConverter로 본문을 작성하기 전에 응답을 사용자 지정할 수 있습니다. 구현은 RequestMappingHandlerAdapter 및 ExceptionHandlerExceptionResolver로 직접 등록되거나 @ControllerAdvice로 주석이 추가될 가능성이 더 높으며 이 경우 둘 다에 의해 자동 감지됩니다.

Http응답 구간에서 메시지를 가공할 수 있는 구간이 존재하고 일부 객체를 직접 구현해 커스텀이 가능한 형태로 되어있다. 해당 객체를 상속하여 원하는 것을 핸들링하면되는데 메시지를 가공하거나, 에러가 발생하였을 때 가공할 수가 있는 것 같다

[간략정리]

  • ResponseBodyAdvice 는 @ResponseBody가 설정된 controller가 return 할 때 실행된다.
  • Exception advice 이후에 실행된다. (설정 순서 조정 가능)
  • @Override 하는 beforeBodyWrite에서 최종적인 후처리를 진행한다.

 

# supports 메소드
이 구성 요소가 지정된 컨트롤러 메서드 반환 유형과 선택한 HttpMessageConverter 유형을 지원하는지 여부.

# beforeBodyWrite 메소드
HttpMessageConverter가 선택된 후 쓰기 메서드가 호출되기 직전에 호출됩니다.

반응형