ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • spring @ControllerAdvice, @ExceptionHandler
    Spring/Spring 기본 지식 2021. 4. 7. 15:27
    반응형

    코딩을 하다가 몇개의 Exception은 처리가 쉬운게 이것이 여러개인 경우 또는 Error Message 처리는 어떻게 할까? 라는 의문으로 시작하게 되어 Advice를 공부하게 되었다

     

     

    코드를 작성을 하게 되면 Exception처리가 매우 길어지거나, 처리하기가 매우 힘들어진다. 그렇기 때문에 스프링에서는 크게 ControllerAdvice, ExceptionHandler 2가지 방법으로 에러처리를 진행한다. 

     

     

    해당코드는 gitHub 확인해주세요

    ExceptionHandler

    ControllerRestController에서 발생되는 에러를 메소스에서 처리하는 기능을 가졌다. 하지만 단점이 있는데 단점을 알아 보자.

    단점

    • Controller 또는 RestController에서만 작동이 된다.
    • ExceptionHandler를 등록해 줘야 한다.
    • Cotnroller나 RestController에 에러가 외부에서 작동이 되지 않는다.

    코드

    ExceptionHandlerController

    @RestController
    @RequiredArgsConstructor
    public class ExceptionHandlerController {
        private final ExceptionHandlerService service;
    
        @GetMapping("/v1")
        public ResponseEntity playNullPointException() {
            return new ResponseEntity<>(service.nullPointExceptionError(), HttpStatus.OK);
        }
    
        @GetMapping("/v1/sample")
        public ResponseEntity playSampleException() {
            return new ResponseEntity<>(service.sampleException(), HttpStatus.OK);
        }
    
        @ExceptionHandler({NullPointException.class})
        public String nullPointExceptionHandler() {
    
            return "Null 에러 발생";
        }
    }

    ExceptionHandlerController2

    @RestController
    @RequiredArgsConstructor
    public class ExceptionHandlerController2 {
        private final ExceptionHandlerService service;
    
        @GetMapping("/v2")
        public ResponseEntity playNullPointException(){
            return new ResponseEntity<>(service.nullPointExceptionError(), HttpStatus.OK);
        }
    
    
        @GetMapping("/v2/sample")
        public ResponseEntity playSampleException(){
            return new ResponseEntity<>(service.sampleException(), HttpStatus.OK);
        }
    
        @ExceptionHandler({NullPointException.class, SampleException.class})
        public String nullPointExceptionHandler(){
            return "Null 또는 SampleException 에러 발생";
        }
    }

    ExceptionHandlerService

    @Service
    public class ExceptionHandlerServiceImpl implements ExceptionHandlerService{
    
        @Override
        public SampleDto nullPointExceptionError() {
            throw new NullPointException("NullPointException 발생");
        }
    
        @Override
        public SampleDto sampleException() {
            throw new SampleException("SampleException 발생");
        }
    }

    Exception 들

    public class SampleException extends RuntimeException{
        public SampleException(String message) {
            super(message);
        }
    }
    
    
    public class NullPointException extends RuntimeException{
        public NullPointException(String message) {
            super(message);
        }
    }

     

    코드 설명 및 실행 결과

    ExceptionHandlerControllerExceptionHandlerController2의 코드를 보게 된다면 SampleException처리 했냐 안했냐의 차이점이 존재한다. 이를 기억하고 코드를 실행시키면 실행결과가 아래와 같이 발생이 된다.

     

     

    NullpointException 처리한 부분

     

    NullpointException 처리한 부분

     

     

    SampleException 을 처리하지 못함

     

    SampleException을 처리 함

     

     

     

    @ControllerAdvice

    ExceptionHandler와 다르게 Exception들을 한군데서 처리할 수 있는 기능을 가졌다.

     

    @ControllerAdvice
    public class GlobalExceptionHandler {
        /**
         * javax.validation.Valid or @Validated 으로 binding error 발생시 발생한다.
         * HttpMessageConverter 에서 등록한 HttpMessageConverter binding 못할경우 발생
         * 주로 @RequestBody, @RequestPart 어노테이션에서 발생
         */
        @ExceptionHandler(MethodArgumentNotValidException.class)
        protected ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
            log.error("handleMethodArgumentNotValidException", e);
            ErrorsResource resource = new ErrorsResource(ErrorCode.INVALID_INPUT_VALUE, e);
            return new ResponseEntity<>(resource, HttpStatus.BAD_REQUEST);
        }
    
        /**
         * @ModelAttribut 으로 binding error 발생시 BindException 발생한다.
         * ref https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-modelattrib-method-args
         */
        @ExceptionHandler(BindException.class)
        protected ResponseEntity handleBindException(BindException e) {
            log.error("handleBindException", e);
            ErrorsResource resource = new ErrorsResource(ErrorCode.INVALID_INPUT_VALUE, e);
            return new ResponseEntity<>(resource, HttpStatus.BAD_REQUEST);
        }
    
        /**
         * enum type 일치하지 않아 binding 못할 경우 발생
         * 주로 @RequestParam enum으로 binding 못했을 경우 발생
         */
        @ExceptionHandler(MethodArgumentTypeMismatchException.class)
        protected ResponseEntity handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
            log.error("handleMethodArgumentTypeMismatchException", e);
            ErrorsResource resource = new ErrorsResource(ErrorCode.METHOD_NOT_ALLOWED, (Errors) e);
            return new ResponseEntity<>(resource, HttpStatus.BAD_REQUEST);
        }
    
        /**
         * 지원하지 않은 HTTP method 호출 할 경우 발생
         */
        @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
        protected ResponseEntity handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
            log.error("handleHttpRequestMethodNotSupportedException", e);
            ErrorsResource resource = new ErrorsResource(ErrorCode.METHOD_NOT_ALLOWED);
            return new ResponseEntity<>(resource, HttpStatus.METHOD_NOT_ALLOWED);
        }
    
        /**
         * Authentication 객체가 필요한 권한을 보유하지 않은 경우 발생합
         */
        @ExceptionHandler(AccessDeniedException.class)
        protected ResponseEntity handleAccessDeniedException(AccessDeniedException e) {
            log.error("handleAccessDeniedException", e);
            ErrorsResource resource = new ErrorsResource(ErrorCode.HANDLE_ACCESS_DENIED);
            return new ResponseEntity<>(resource, HttpStatus.valueOf(ErrorCode.HANDLE_ACCESS_DENIED.getStatus()));
        }
    
        /**
         * RestApi 통신시 파라미터가 다른경우 발생함
         * @param e
         * @return
         */
        @ExceptionHandler(HttpMessageNotReadableException.class)
        protected ResponseEntity handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
            log.error("handleHttpMessageNotReadableException", e);
            ErrorsResource resource = new ErrorsResource(ErrorCode.PARAMETER_BIND_ERROR);
            return new ResponseEntity<>(resource, HttpStatus.valueOf(ErrorCode.HANDLE_ACCESS_DENIED.getStatus()));
        }
    
        @ExceptionHandler(BusinessException.class)
        protected ResponseEntity handleBusinessException(BusinessException e) {
            log.error("handleEntityNotFoundException", e);
            final ErrorCode errorCode = e.getErrorCode();
            ErrorsResource resource = new ErrorsResource(errorCode);
            return new ResponseEntity<>(resource, HttpStatus.valueOf(errorCode.getStatus()));
        }
    
    
        @ExceptionHandler(Exception.class)
        protected ResponseEntity handleException(Exception e) {
            log.error("handleEntityNotFoundException", e);
            ErrorsResource resource = new ErrorsResource(ErrorCode.INTERNAL_SERVER_ERROR);
            return new ResponseEntity<>(resource, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

     

     

     

     

     

     

     

    참고

    cheese10yun.github.io/spring-guide-exception/

    반응형
Designed by Tistory.