본문 바로가기
Spring

[Spring] 예외처리 - 공통화(@RestControllerAdvice)

by Bhinney 2022. 10. 27.

❗️직전에 포스팅한 내용처럼 예외를 처리하면 문제가 발생하기 때문에, 예외를 공통으로 처리하려고 한다.❗️

 

[Spring] 예외 처리 - Controller에서 처리

❗️여기는 예외 처리를 Controller에서 해본 것이다. ❗️ 이렇게 유효성 검증과 같이 잘못된 요청이 들어가면 에러가 발생합니다. 이 때, 이 에러가 왜 발생했는지 위의 사진에서는 알 수 없다. 

bhinney.tistory.com


✅ @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());
		}
	}
}

GlobalExceptionAdvice 클래스를 이용해 예외를 처리했을 때 나오는 응답 에러

※ FieldError나 BindingResult의 관한 정보는 이전 포스팅 참조


 

댓글