이전 글에서 배운 DTO 클래스의 유효성 검증을 통해 위 동그라미 부분처럼 Email 형식을 지키지 않던가, 아무 값도 들어있지 않다면 그 아래처럼 에러 메시지가 전송된다는 것을 확인할 수 있었습니다.
2022.06.28 - [CodeStates/Spring MVC] - [자바 Spring] DTO에 대해 배워보자
[자바 Spring] DTO에 대해 배워보자
DTO(Data Transfer Object)란 계층간 데이터 교환을 위해 사용하는 객체(Java Beans)입니다. DTO 중 T인 Transfer라는 의미에서 알 수 있듯이 데이터를 전송하기 위한 용도의 객체 정도로 생각할 수 있습니다.
jungdo8016.tistory.com
효과적인 예외처리를 위해 별도의 클래스 하나를 생성하겠습니다.
@Getter
@AllArgsConstructor
public class ErrorResponse {
// (1)
private List<FieldError> fieldErrors;
@Getter
@AllArgsConstructor
public static class FieldError {
private String field;
private Object rejectedValue;
private String reason;
}
}
먼저 FieldError 클래스를 ErrorResponse 클래스의 내부 클래스로 설정하였습니다.
이유는 예외처리 해야할 변수의 종류가 한 가지가 아니기 떄문입니다. 예를 들어 이전 자료를 보면 email과 name, phone 변수의 DTO 유효성 검증에서 세개 모두 유효성을 위반되는 것을 확인할 수 있었습니다.
그럼 예외처리를 총 3번하여 결과를 클라이언트에게 응답하여야 합니다. 따라서 ErrorResponse 클래스의 필드인 fieldErrors 리스트의 각 요소는 하나하나는 하나의 예외처리라고 인지하면 될 것 같습니다.
@RestController
@RequestMapping("/v7/members")
@Validated
@Slf4j
public class MemberController {
...
...
@PostMapping
public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberDto) {
Member member = mapper.memberPostDtoToMember(memberDto);
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)
List<ErrorResponse.FieldError> errors =
fieldErrors.stream()
.map(error -> new ErrorResponse.FieldError(
error.getField(),
error.getRejectedValue(),
error.getDefaultMessage()))
.collect(Collectors.toList());
return new ResponseEntity<>(new ErrorResponse(errors), HttpStatus.BAD_REQUEST);
}
}
위 코드의 @ExceptionHandler는 예외 처리 메소드라는 의미를 가지고 있습니다. handleException 메소드 위의 Controller 메소드 들에서 DTO가 유효성 검증에 실패했을 때, 자동으로 해당 메소드가 실행되어 (1)의 fieldErrors 리스트에 정보가 전달 되게 됩니다.
그 정보들을 이전에 작성하였던 ErrorResponse 클래스 내부의 FieldError 클래스에 일부 필요한 정보만 전달하여 새로운 리스트를 생성합니다.
마지막으로 ResponseEntity<>()를 통해 예외 처리 결과를 BAD_REQUEST 상태 메시지와 함께 반환하면 마치게 됩니다.
위처럼 어떤 부분에서 유효성 검증 실패가 일어났는지 이유와 항목이 반환되게 됩니다.
# ExceptionHandler의 단점
1. 각각의 Controller 클래스에서 @ExceptionHandler 애너테이션을 사용하여 Request Body에 대한 유효성 검증 실패에 대한 에러 처리를 해야되므로 각 Controller 클래스마다 코드 중복이 발생합니다.
2. Controller에서 처리해야 되는 예외(Exception)가 유효성 검증 실패에 대한 예외 (MethodArgumentNotValidException)만 있는것이 아니기 때문에 하나의 Controller 클래스 내에서 @ExceptionHandler를 추가한 에러 처리 핸들러 메서드가 늘어납니다.
# RestControllerAdvice를 사용한 예외 처리 공통화
위 같이 @ExceptionHandler의 중복되는 단점들을 해결하기 위해 적용된 개념이 @RestControllerAdvice 입니다.
@RestControllerAdvice 애너테이션을 추가한 클래스를 이용하면 예외 처리를 공통화 할 수 있습니다.
@RestControllerAdvice
public class GlobalExceptionAdvice {
// (1)
@ExceptionHandler
public ResponseEntity handleMethodArgumentNotValidException(
MethodArgumentNotValidException e) {
final List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
List<ErrorResponse.FieldError> errors =
fieldErrors.stream()
.map(error -> new ErrorResponse.FieldError(
error.getField(),
error.getRejectedValue(),
error.getDefaultMessage()))
.collect(Collectors.toList());
return new ResponseEntity<>(new ErrorResponse(errors), HttpStatus.BAD_REQUEST);
}
}
이전에 Controller 내부마다 존재했던 handleMethodArgumentNotValidException 메소드를 @RestControllerAdvice가 붙어있는 별도의 클래스에 새롭게 작성하였습니다.
'JAVA > Spring MVC' 카테고리의 다른 글
[자바 intellij] @RequestParam? @ModelAttribute? @RequestBody는 또 뭐야?? (0) | 2022.08.14 |
---|---|
[Spring 자바] Mapper에 대해 알아보자 (1) | 2022.06.29 |
[자바 Spring] DTO에 대해 배워보자 (1) | 2022.06.28 |
[자바 Spring] HTTP Request 헤더(Header) 정보 얻기 (0) | 2022.06.26 |
자바 Spring MVC의 개념에 대해 알아보자 (0) | 2022.06.25 |