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()로 초기화하고 리스트로 반환
- FieldError
- 멤버 변수 선언
- @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 |