본문 바로가기
Backend boot camp/Session3

[Spring MVC] Exception Handle

by orioncsy 2022. 10. 29.

Spring MVC 예외처리

@ExceptionHandler

사용 목적

  • DTO 유효성 검증만으로 Response Body에서 어떤 항목이 유효성 검증 실패인지 확인 불가

Controller에서 예외 처리

  • response Body는 애플리케이션에서 예외가 발생했을 때 내부 Spring에서 전송해주는 응답 메시지
  • @ExceptionHandler 애너테이션을 사용
  • public ResponseEntity handleException(MethodArgumentNotValidException e)
    • MethodArgumentNotValidException으로 에러를 받는다
  • final List <FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
    • 에러에 대한 정보를 확인 가능
  • return new ResponseEntity<>(fieldErros, HttpStatus.BAD_REQUEST);
    • 에러 정보를 Response Body

ErrorResponse class 생성

  • 특정 정보만을 담아 응답하기 위해 새로운 클래스를 생성
  • 새로운 class(ErrorResponse)에 멤버 변수로 List<FieldError>를 만든다
  • 새로운 클래스 안에 static 맴버 클래스로 FieldError를 만들어 원하는 정보를 설정
  • @ExceptionHandler에 e.getBindingResult().getFieldErrors(); 로 List<FieldError>를 받는다
  • List<ErrorResponse.FieldError>를 생성해서 List<FieldError>를 스트림으로하여 원하는 정보를 담아서 리스트로 변환하여 할당
  • ResponseEntity에 new ErrorResponse([생성한 리스트])로 응답을 전송

@ExceptionHandler 단점

  • 각 Controller에 애너테이션 처리로 코드 중복
  • 유효성 검증 실패에 대한 예외(MethodArgumentNotValidException)만 있는 것이 아니기 때문에 추가하려고 한다면 코드가 늘어난다.
  • ConstraintViolationException 등 다른 예외를 받을 경우 다른 방식으로 handler를 처리해야 한다

@RestControllerAdvice

예외 처리 공통화

  • 특정 클래스에 @RestControllerAdvice를 추가 하면 여러 개의 Controller에 @ExceptionHandler, @InitBinder, @ModelAttribute가 추가된 메서드 공유 가능
  • SSR에서 @IntitBinder, @ModelAttribute를 사용

@RestControllerAdvice

  • 각 controller에서 @ExceptionHandler가 붙은 메서드 제거
  • 따로 패키지를 선언해 클래스를 생성 후 @RestControllerAdvice를 붙인다
  • 새로 생성한 클래스 안에 @ExceptionHandler로 작성했던 로직을 그대로 넣는다

여러 개의 예외 처리

  • ErrorResponse라는 클래스를 생성(@Getter)
    • 멤버 변수 선언
      • private List <FieldError> fieldErrors
      • private List <ConstraintViolationErrorr> violationErros
    • 생성자는 private으로 수동으로 생성하여 final로 각각의 필드 멤버를 넣는다
    • 생성자를 대체할 static 메서드 of 생성
      • BindingResult를 매개로 받아 ErrorResponse 객체를 반환
      • Set <ConstraintViolation <?>>을 매개로 받아 ErrorResponse 객체를 반환
    • 두 개의 static 클래스 멤버 선언(@Getter)
      • FieldError
        • 필드 멤버로 field, rejectedValue, reason
        • 생성자는 private으로 선언
        • BindingResult를 매개로 받아 List <FieldError>를 반환하는 static 메서드 of 생성
          • bindingResult.getFieldErrors()로 List <org.springframework.validation.FiledError> 생성
          • 스트림으로 getField(), getRejectedValue(), getDefaultMessage() 가져와서 FieldError로 map 하고 리스트 생성하고 반환
      • ConstraintViolationError
        • 필드 멤버로 propertyPath, rejectedValue, reason
        • 생성자는 private으로 선언
        • Set <ConstraintViolation <?>> 을 매개로 받아 List <ConstraintViolationError>을 반환하는 static 메서드 of 생성
          • constraintViolations를 스트림으로 생성
          • ConstraintViolationError 객체 생성하여 getPropertyPath(), getInvalidValue(), getMessage()로 초기화하고 리스트로 반환
  • @RestControllerAdvice를 붙인 클래스 생성
  • @ExceptionHandler, @ResponseStatus(HttpStatus.BAD_REQUEST)를 붙인 두 개 메서드 선언
    • MethodArgumentNotValidException을 받아서 ErrorResponse 객체를 생성해서 반환
    • ConstraintViolationException을 받아서 ErrorResponse 객체를 생성해서 반환

비즈니스 로직 예외처리

비즈니스적 예외 던지기 및 예외처리

Checked Exception, Unchecked Exception

  • Checked Exception
    • 예외를 잡아서 처리를 해야 하는 예외
    • ex) ClassNotFoundExceptioin
  • Unchecked Exception
    • 예외를 잡아서 어떤 처리를 할 필요가 없는 예외
    • ex) NullPointException, ArrayIndexOutOfBoundsException
    • 개발자가 코드를 잘못 작성한 경우 RuntimeException을 상속한 예외

개발자가 의도적으로 예외를 던지는 상황

  • 백엔드 서버와 외부 시스템 연동에서 발생하는 에러 처리
  • 시스템 내부에서 조회하려는 리소스가 없는 경우

의도적 예외 던지고 받기

  • throw를 통해 서비스 계층에서 예외를 던지면 API 계층에서 예외를 받는다
  • Exception Advice로 예외를 처리하게 한다면 예외도 이곳에서 처리한다
  • 서비스 계층에서 throw new RuntimeException(”message”)
  • Exception advice에서 @ExceptionHandler, @Response(HttpStatus.NOT_FOUND) 붙인 메서드 생성 후 처리

Custom Exception

  • enum을 생성하여 예외 코드를 넣고 멤버로 status code랑 message를 넣는다
  • custom exception을 위해 클래스 생성하여 RuntimeException을 상속
    • 필드 맴버로 execption code 선언
    • 생성자는 exception code를 넣고 super에 exceptionCode의 messge를 넣는다.
  • 서비스 계층에서 custom exception을 throw 한다
  • exception advice에서 예외 처리
    • 다양한 종류의 예외를 받아야 하기 때문에 HttpStatus.valueOf(e.getExceptionCode().getStatus를 래핑 하여 ResponseEntity로 반환

'Backend boot camp > Session3' 카테고리의 다른 글

[Spring MVC] 트랜잭션  (0) 2022.11.07
[Spring MVC] JPA 데이터 액세스 계층  (0) 2022.11.07
[Spring MVC] JDBC DB Access Layer  (0) 2022.10.29
[Spring MVC] Service layer  (0) 2022.10.29
[Spring MVC] API Layer  (0) 2022.10.29