❗️직전에 포스팅한 내용처럼 예외를 처리하면 문제가 발생하기 때문에, 예외를 공통으로 처리하려고 한다.❗️
✅ @ControllerAdvice
- @ExceptionHandler, @InitBinder, @ModelAttribute가 적용된 메서드들에 AOP를 적용해 Controller에 적용하기 위해 고안된 어노테이션
- 클래스에 선언 + 전역적으로 발생하는 예외를 잡아서 처리할 수 있다.
- @Component 어노테이션이 포함되어 있어 빈으로 관리할 수 있다.
✅ @RestControllerAdvice
- @ControllerAdvice + @ResponseBody
- @ControllerAdvice와 동일한 역할을 하고, 응답 객체를 리턴할 수 있다.
- 전역으로 예외처리를 구현할 클래스에 @RestControllerAdvice를 설정해준다.
- 각각 응답 객체로 받고, 이 응답을 받기 위해 ErrorResponse를 수정해준다.
- 이전 포스팅에서 Controller에서 구현했던 원하는 정보만 받는 부분(stream으로 구현했던 것)을 ErrorResponse 내부 클래스에서 객체를 생성해준다.
@RestControllerAdvice
public class GlobalExceptionAdvice {
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleMethodArgumentNotValidException(
MethodArgumentNotValidException e) {
final ErrorResponse response = ErrorResponse.of(e.getBindingResult());
return response;
}
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleConstraintViolationException(
ConstraintViolationException e) {
final ErrorResponse response = ErrorResponse.of(e.getConstraintViolations());
return response;
}
}
@Getter
public class ErrorResponse {
// MethodArgumentNotValidException 에러
private List<FieldError> fieldErrors;
// URI 변수 값의 유효성 검증에 실패로 발생한 에러
private List<ConstraintViolationError> violationErrors;
// private --> new 생성자 방식으로 생성자 생성 불가
private ErrorResponse(final List<FieldError> fieldErrors,
final List<ConstraintViolationError> violationErrors) {
this.fieldErrors = fieldErrors;
this.violationErrors = violationErrors;
}
// BindingResult 에 대한 ErrorResponse 객체 생성
public static ErrorResponse of(BindingResult bindingResult) {
return new ErrorResponse(FieldError.of(bindingResult), null);
}
// Set<ConstraintViolation<?>> 객체에 대한 ErrorResponse 객체 생성
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());
}
}
}
※ FieldError나 BindingResult의 관한 정보는 이전 포스팅 참조
'Spring' 카테고리의 다른 글
[Spring] Spring boot Java 카카오 페이 단건 결제 구현하기 (0) | 2022.12.08 |
---|---|
[Spring] 예외 처리 - 사용자 정의 (0) | 2022.10.27 |
[Spring] 예외 처리 - Controller에서 처리 (0) | 2022.10.27 |
[Spring] MapStruct 사용하여 Mapper 구현하기 (0) | 2022.10.20 |
[Spring Framework] ResponseEntity (0) | 2022.08.25 |
댓글