파워노트

[ skeleton ] spring boot exception ( 예외 처리 ) 본문

spring boot

[ skeleton ] spring boot exception ( 예외 처리 )

파워킴 2021. 11. 28. 19:24
반응형

Exception 처리 .

  • spring boot에서의 Exception 처리는  복잡하게 파고들면 복잡하지만 간단하게 생각하면 또 간단하다. ( 당연한 말이겠지만 )
  • Exception 은 크게 
    • 컴파일 시점에 발생하는 예외를 Exception(일반예외) 라고 하고, 
    • 프로그램 실행시에 발생하는 예외를 RuntimeException(실행예외) 라고 합니다. 
    • 즉, 예외가 발생하는 시점에 프로그램이 실행 전 후 상태에 따라 이를 구분하면 됩니다. 컴파일 시 예외처리를 확인하는 차이일 뿐, 두 가지 예외는 모두 예외 처리가 필요하다. 자바에서는 예외를 클래스로 관리한다. 모든 예외 클래스들은 java.lang.Exception 클래스를 상속받는다. 

 

 

 

ExceptionHandler   

  • @ControllerAdvice, @RestControllerAdvice 에서 controller 내부에서 처리되다가 오류나는 사항에 대해서 Handling 할 수 있다. 
  • Web Page Handle
  • @ControllerAdvice(basePackages = "com.powernote.skeleton.controller")
    public class WebExceptionHandler {
    
        // error.html이 있는경우는 해당 페이지로 이동하고 없는경우 error/4xx.html error/5xx.html 로 찾아 status 를 찾아 이동한다.
        // page가 있는경우( 404 등 ) BasicErrorController 를 통하지 않는다.
        @ExceptionHandler(value = Exception.class)
        public ModelAndView pageHandlerException(HttpServletRequest request, Model model, Exception e) {
            String defaultError = MessageType.ERROR_TYPE_000.toString();
            log.info("WebExceptionHandler  ");
    
            model.addAttribute("timestamp",   new Date().toString()  );
            model.addAttribute( "status", String.valueOf( HttpStatus.INTERNAL_SERVER_ERROR.value() ) );
            model.addAttribute( "error", HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase() );
            model.addAttribute( "errorCode",   defaultError );
            model.addAttribute( "message", MessageUtil.getMessage( defaultError ) );   // MessageUtil을 통한 메시지 전달 처리 적용 필요.
            model.addAttribute( "path", request.getRequestURI() );
            model.addAttribute( "exception", Exception.class.getName() );
    
            log.info( "[ Exception, " + String.valueOf( HttpStatus.INTERNAL_SERVER_ERROR.value() ) +   MessageUtil.getMessage( defaultError ) + "]");
            return new ModelAndView("/error/error");
        }
    
    }
  • API Handler 
    @RestControllerAdvice(annotations = RestController.class)
    public class ApiExceptionHandler {
    
        // 기본 에러 처리
        @ExceptionHandler(Exception.class)
        public ResponseEntity<ErrorRes> exception(HttpServletRequest request, Exception e) {
            String defaultError = MessageType.ERROR_TYPE_000.toString();
            ErrorRes response = ErrorRes.builder().build();
    
            response.setTimestamp(  new Date().toString()  );
            response.setStatus( String.valueOf( HttpStatus.INTERNAL_SERVER_ERROR.value() ) );
            response.setError( HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase() );
            response.setErrorCode( defaultError );
            response.setMessage(  MessageUtil.getMessage( defaultError ) );
            response.setPath( request.getRequestURI() );
    
            return  new ResponseEntity<>(response, CommonUtil.getHeader(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
        
        
    }​
  • controller 내부 구현시 오류 발생하면 해당 mvc controll 인경우 WebExceptionHandler 의 Handler에서 처리 되며, API 요청인경우 ApiExceptionHandler 에서 처리 된다 .
  • 404 error , 403 error 등과 같이 controller 내부에 까지 오기 전에 servlet 단에서 오류가 난경우의 처리를 위해 getErrorAttributes() 메소드를 override 해서 구현한다. 
    @Component
    public class ExceptionServletAttributes extends DefaultErrorAttributes {
    
        @Override
        public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
            Integer status = (Integer) webRequest.getAttribute("javax.servlet.error.status_code", RequestAttributes.SCOPE_REQUEST);
     
            if (status != HttpStatus.NOT_FOUND.value() && status != HttpStatus.FORBIDDEN.value()  ) {
               return super.getErrorAttributes(webRequest, options);
            }
    
            Map<String, Object> errorAttributes = new HashMap<>();
    
            if( status == HttpStatus.NOT_FOUND.value() ){
                errorAttributes.put("timestamp", new Date().toString() );
                errorAttributes.put("status", HttpStatus.NOT_FOUND.value());
                errorAttributes.put("error", "404");
                errorAttributes.put("message",   MessageType.ERROR_PAGE_404.getMessage() );
                String  attributeNames = String.valueOf(webRequest.getAttribute("javax.servlet.forward.request_uri", 0)) ;
                errorAttributes.put("path", attributeNames );
                errorAttributes.put("errorCode", MessageType.ERROR_PAGE_404.toString() );
                errorAttributes.put("exception", "");
            }
            else if ( status == HttpStatus.FORBIDDEN.value() ) {
                errorAttributes.put("timestamp", new Date().toString() );
                errorAttributes.put("status", HttpStatus.FORBIDDEN.value());
                errorAttributes.put("error", "403");
                errorAttributes.put("message", MessageType.ERROR_PAGE_403.getMessage() );
                String  attributeNames = String.valueOf(webRequest.getAttribute("javax.servlet.forward.request_uri", 0)) ;
                errorAttributes.put("path", attributeNames );
                errorAttributes.put("errorCode", MessageType.ERROR_PAGE_403.toString() );
                errorAttributes.put("exception", "");
            }
     
            return errorAttributes;
        }
    }​

 

 

error page 구현

  • spring boot에서의 Error 처리설정 및 경로 설정은 BasicErrorController 에서 설정된다. 기본값은  "/error"

 

마무리 .

  • 개인적으로 Exception 처리의 정리가 spring boot 개발에서는 중요한 부분으로 생각된다. 
  • 물론 완벽한 코드를 처리하면 좋지만  언제나 오류는 발생할 수 있기에 합당하면서도 에러확인이 편하게 도출되고 표현되도록 하는것은 매우 중요하다. 

 

반응형
Comments