카테고리 없음

@RestControllerAdvice를 사용한 예외 처리 공통화

더덕2 2023. 8. 30. 17:41

예외 처리 공통화를 하는 이유

@ExceptionHandler만 사용하여 Controller에 작성할 경우

여러 예외로 인해 각 Controller마다 추가해야할 예외처리가 너무많아지며, 중복도 발생하기 때문에

예외처리 공통화를 통해 더 효율적으로 관리 할 수 있다.

 

@RestControllerAdvice

예외를 처리할 ExceptionAdvice 클래스에 @RestControllerAdvice 애너테이션을 추가하면 이 클래스는 이제 Controller 클래스에서 발생하는 예외를 도맡아서 처리하게 됩니다.

 

 

지금까지 Controller 클래스에서 발생하는 RequestBody의 유효성 검증에 대한 에러를 처리하였는데

URI 변수로 넘어오는 값의 유효성 검증에 대한 에러 처리는 어떻게 할 수 있을까

 

ConstraintViolationException 에 대한 처리도 할 수 있도록 ErrorResponse 클래스를 수정해보자

 

@Getter
public class ErrorResponse {
	//MethodArgumentNotValidException(DTO 멤버 변수 필드의 유효성 검증에 대한 에러)
    private List<FieldError> fieldErrors;
    
    //ConstraintViolationException(URI 변수 값의 유효성 검증에 실패로 발생한 에러)
    private  List<ConstraintViolationError> violationErrors; 


	// private 생성자
    // ErrorResponse의 객체를 생성함과 동시에 Error의 역할을 명확하게 해주기 위해서 of를 사용
    private ErrorResponse(List<FieldError> fieldErrors, List<ConstraintViolationError> violationErrors){
        this.fieldErrors = fieldErrors;
        this.violationErrors = violationErrors;
    }
	
    // MethodArgumentNotValidException 에서 정보를 얻기위해 필요한게 BindingResult객체
    public static ErrorResponse of(BindingResult bindingResult){
        return new ErrorResponse(FieldError.of(bindingResult),null);
    }

	// ConstraintViolationException 에서 에러 정보를 얻기 위해 필요한 것이 
    // Set<ConstraintViolation<?>> 객체
    public static ErrorResponse of(Set<ConstraintViolation<?>> violations) {
        return new ErrorResponse(null, ConstraintViolationError.of(violations));
    }

    @Getter
    public static class FieldError{
        private String field;
        private Object rejectedValue;
        private String reason;

        private FieldError(String field, Object rejectedValue, String reason){
            this.field = field;
            this.rejectedValue = rejectedValue;
            this.reason = reason;
        }

        public static List<FieldError> of(BindingResult bindingResult){
            final List<org.springframework.validation.FieldError> fieldErrors =
                    bindingResult.getFieldErrors();
            return fieldErrors.stream()
                    .map(error -> new FieldError(
                            error.getField(),
                            error.getRejectedValue() == null ?
                                    "" : error.getRejectedValue().toString(),
                            error.getDefaultMessage()))
                    .collect(Collectors.toList());
        }
    }

    @Getter
    public static class ConstraintViolationError {
        private String propertyPath;
        private Object rejectedValue;
        private String reason;

        private ConstraintViolationError(String propertyPath, Object rejectedValue,
                                         String reason) {
            this.propertyPath = propertyPath;
            this.rejectedValue = rejectedValue;
            this.reason = reason;
        }

        public static List<ConstraintViolationError> of(
                Set<ConstraintViolation<?>> constraintViolations) {
            return constraintViolations.stream()
                    .map(constraintViolation -> new ConstraintViolationError(
                            constraintViolation.getPropertyPath().toString(),
                            constraintViolation.getInvalidValue().toString(),
                            constraintViolation.getMessage()
                    )).collect(Collectors.toList());
        }
    }

ErrorResponse 클래스에 ConstraintViolationException에 대한 Error Response까지 생성 가능하도록 수정된 코드이다