예외 처리 공통화를 하는 이유
@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까지 생성 가능하도록 수정된 코드이다