@RestControllerAdvice

Lim MyeongSeop·2022년 11월 26일
0
post-thumbnail

제가 다니는 회사에서는 클라우드 서비스를 MSA로 구성하는 과정에서 service를 제공하는 서버끼리는 서로 RestTemplate을 통해 통신을 하게 됩니다.

이 과정에서 예외 처리 과정에서 @RestControllerAdvice를 적용해보는 경험을 하게 되어 이 글을 작성하게 되었습니다.


예외가 발생했을 때

회사에서 제공할 서비스는 Open API를 제외하고는 내부 응답 상태 코드를 사용하게 됩니다.

자체 Response 객체가 가지는 코드와 메시지를 가지고 통신 결과를 판단하고 있습니다. 하지만 예외 처리를 해주지 않는다면 서비스 서버는 커스텀 예외을 던지게 되어 500 에러가 발생하게 됩니다.

  • 자체 Response 객체
    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    @Getter
    public class CustomResponse<T> {
        private int code;
        private String message;
        private T result;
        private List<T> results;
    
        public CustomResponse(int code, String message) {
            this(code, message, null);
        }
    
        public CustomResponse(int code, String message, T result) {
            this(code, message, result, new ArrayList<>());
        }
    }
  • 커스텀 예외 및 내부 응답 코드
    @Getter
    public class CustomException extends RuntimeException {
        private ResponseCode code;
    
        public CustomException(ResponseCode code, String message) {
            super(message);
            this.code = code;
        }
    }
    
    @Getter
    public enum ResponseCode {
        NONE_CONTENTS(402);
    
        private int httpCode;
    
        ResponseCode(int httpCode) {
            this.httpCode = httpCode;
        }
    }
@RestController
public class PostController {

    @GetMapping("/call")
    public CustomResponse<String> callServer() {
        throw new CustomException(ResponseCode.NONE_CONTENTS, "not found contents");
    }
}

@ControllerAdvice

당연히 커스텀 예외를 처리해주기 위해 @ControllerAdvice를 통해 예외 핸들링을 하려 했습니다.

@ControllerAdvice
public class ExceptionAdvice {

    @ExceptionHandler(CustomrException.class)
    public CustomResponse handleCustomException(CustomerException e) {
        return new CustomResponse(e.getCode().getHttpCode(), e.getMessage());
    }
}

단순하게 CustomeException의 code와 message를 CustomeResponse로 전달해주려 했습니다.

하지만, 결과는 500 에러가 발생하게 되었습니다.

분명히 제대로 해준거 같은데 뭐지… 하고 있는데 뒤에 지나가시던 팀장님께 바로 질문을 드려보았습니다.

💡 팀장님 : 이거 `@ResponseBody`가 없네~

일단 팀장님의 말씀대로 @ResponseBody를 붙여주니 제대로 동작하는 것을 확인할 수 있었습니다.

@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {

    @ExceptionHandler(CustomException.class)
    public CustomResponse handleCustomException(CustomException e) {
        return new CustomResponse(e.getCode().getHttpCode(), e.getMessage());
    }
}

그리고 추가적으로 @ControllerAdvice@Controller와 같이 json객체 정보를 전달하지 않고 페이지를 찾아가는 것과 같은 동작을 수행하는 것을 알 수 있었습니다.


@RestControllerAdvice

Intellij 자동완성을 사용하던 중 @ControllerAdvice를 검색하니 @RestControllerAdvice가 검색되었습니다.

spring 4.3부터 추가되었고 @ControllerAdvice@ResponseBody가 적용되어 있는 애너테이션입니다.

바로 적용해서 확인해보니 원하는 결과대로 출력되었습니다.

@RestControllerAdvice
public class ExceptionAdvice {

    @ExceptionHandler(CustomException.class)
    public CustomResponse handleCustomException(CustomException e) {
        return new CustomResponse(e.getCode().getHttpCode(), e.getMessage());
    }
}


@RestControllerAdvice를 적용하는 것은 누구에게는 당연하게 생각할 수 있지만 이렇게 일을 하다가 문제를 처리하는 경험을 하니 다른 분들에게 공유하고 싶다는 생각에 이 글을 작성해 보았습니다.

사실 문제를 재현하는 과정에서 예외 처리를 하지 않고 예외를 던졌을 때 406 에러가 발생하는 것을 보고 한번 멘붕이 왔지만 CustomResponse에 getter를 생성하지 않아서 발생한 에러였습니다.

간단하지만 일을 하면서 만나는 문제에 대해 다시 한번 생각해보는 계기가 되었습니다.

profile
조금 더 좋은 코드를 위해 고민해봅니다.

0개의 댓글