[예외 처리] @ExceptionHandler를 이용한 예외 처리
by mignon251. MemberController에 @ExceptionHandler 적용
- @ExceptionHandler 애너테이션을 이용해 예외를 처리하도록 handleException() 메서드 추가
@RestController
@RequestMapping("/v6/members") // v6: @ExceptionHandler 이용한 예외 처리
@Validated
@Slf4j
public class MemberController {
...
@PostMapping
public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberPostDto) {
Member member = mapper.memberPostDtoToMember(memberPostDto);
Member response = memberService.createMember(member);
return new ResponseEntity<>(mapper.memberToMemberResponseDto(response), HttpStatus.CREATED);
}
...
...
@ExceptionHandler
public ResponseEntity handleException(MethodArgumentNotValidException e) {
// 1
final List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
// 2
return new ResponseEntity<>(fieldErrors, HttpStatus.BAD_REQUEST);
}
}
< Request Body 의 유효성 검증 실패 시 예외 처리 과정 >
- MemberController 에는 @ExceptionHandler 애너테이션이 추가된 상태
- 클라이언트에서 MemberController의 postMember() 핸들러 메서드에 요청 전송 (POST /v6/members)
- RequestBody 에 유효하지 않은 요청 데이터가 포함되어 있어 유효성 검증에 실패
- MethodArgumentNotValidException 발생
- 유효성 검증 과정에서 내부적으로 던져진 MethodArgumentNotValidException 을 handleException() 메서드가 전달받음
- (1) 과 같이 MethodArgmentNotValidException 객체(예외 객체)에서 getBindingResult().getFieldErrors() 를 통해 발생한 에러 정보 확인
- (1) 에서 얻은 에러 정보를 (2)에서 ResponseEntity를 통해 Response Body로 전달
// 아래와 같은 Request Body 로 요청
{
"email": "asdf@email",
"name": "ㅁㄴㅇㄹ",
"phone": "010-1111-1111"
}
// 결과
[
{
"codes": [
"EnhancedEmail.memberPostDto.email",
"EnhancedEmail.email",
"EnhancedEmail.java.lang.String",
"EnhancedEmail"
],
"arguments": [
{
"codes": [
"memberPostDto.email",
"email"
],
"arguments": null,
"defaultMessage": "email",
"code": "email"
}
],
"defaultMessage": "올바른 형식의 이메일 주소여야 합니다.",
"objectName": "memberPostDto",
"field": "email",
"rejectedValue": "asdf@email",
"bindingFailure": false,
"code": "EnhancedEmail"
}
]
=> 원하는 에러 메세지는 담겨있기는 한데... 영 지저분하다.
ErrorResponse 클래스를 적용해 깔끔하게 에러 메세지 받기
1. ErrorResponse 클래스 작성
@Getter
@AllArgsConstructor
public class ErrorResponse {
// 1
private List<FieldError> fieldErrors;
// 2
@Getter
@AllArgsConstructor
public static class FieldError { // 하나의 필드 에러 정보를 담을 클래스
private String field;
private Object rejectedValue;
private String reason;
}
}
- 유효성 검증 실패 시, 실패한 필드(멤버 변수)에 대한 Error 정보만 담아서 응답으로 전송하기 위한 클래스(V1)
- 클라이언트로 전달된 에러 메세지는 JSON 배열의 형태
(유효성 검증에 실패한 멤버 변수가 여러 개일 수 있으므로...) - 따라서 List 객체 이용! => List<ErrorResponse.FieldError>
2. ErrorResponse 객체를 응답으로 전송 - MemberController handleException() 수정
@ExceptionHandler
public ResponseEntity handleException(MethodArgumentNotValidException e) {
// 1
final List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
// 2
List<ErrorResponse.FieldError> errors =
fieldErrors.stream()
.map(error -> new ErrorResponse.FieldError(
error.getField(),
error.getRejectedValue(),
error.getDefaultMessage()))
.collect(Collectors.toList());
return new ResponseEntity<>(fieldErrors, HttpStatus.BAD_REQUEST);
}

@ExceptionHandler의 단점
- 각각의 Controller 클래스에서 @ExceptionHandler 애너테이션을 사용해 Request Body 에 대한 유효성 검증 실패에 대한 예외처리를 해야 한다.
=> 각 Controller 클래스마다 코드 중복 발생 - Controller에서 처리해야 하는 예외(Exception)가 유효성 검증 실패에 대한 예외(MethodArgumentNotValidException)만 있는 것이 아니다.
=> @ExceptionHandler 를 추가한 에러 처리 핸들러 메서드가 하나의 Controller 클래스 내에 여러 개가 될 수 있다.
=> 코드 복잡도 증가
@ExceptionHandler
public ResponseEntity handleException(MethodArgumentNotValidException e) {
// 1
final List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
// 2
List<ErrorResponse.FieldError> errors =
fieldErrors.stream()
.map(error -> new ErrorResponse.FieldError(
error.getField(),
error.getRejectedValue(),
error.getDefaultMessage()))
.collect(Collectors.toList());
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler
public ResponseEntity handleException(ConstraintViolationException e) {
/**
* - ConstraintViolationException 클래스는 getBindingResult().getFieldErrors()
* 와 같이 에러 정보를 얻을 수 없다.
* - MethodArgumentNotValidException과 다르게 또 다른 방식으로 처리가 필요.
*/
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
@ExceptionHandler
...
Additional Keywords
- @ExceptionHandler
- MehodArgumentNotValidException 에서 에러 정보를 담고 있는 BindingResult 클래스
- ConstraintViolationException 에서 에러 정보를 담고 있는 ConstraintViolation 인터페이스와 구현 클래스
'Spring' 카테고리의 다른 글
| [예외 처리] @RestControllerAdvice 사용한 예외 처리 (0) | 2023.04.20 |
|---|---|
| [예외 처리] 비즈니스 로직에 대한 예외 처리 (0) | 2023.04.20 |
| DTO 유효성 검증 (Validation) - (1) (0) | 2023.04.18 |
| HTTP 요청/응답에서의 DTO(Data Transfer Object) (0) | 2023.04.17 |
| Rest Client, RestTemplate (0) | 2023.04.17 |
블로그의 정보
Mignon'S Dev Log
mignon25