@RestControllerAdvice - 유효성 검사

박영준·2023년 7월 20일
0

Spring

목록 보기
47/58

1. 문제점

SignupRequestDto

@Getter
@NoArgsConstructor
public class SignupRequestDto {
    @NotBlank
    @Size(min = 4,max = 10, message ="작성자명은 4자 이상 10자 이하만 가능합니다.")
    @Pattern(regexp = "^[a-z0-9]*$", message = "작성자명은 알파벳 소문자, 숫자만 사용 가능합니다.")
    private String username;

    @NotBlank
    @Size(min = 8,max = 15, message ="비밀번호는 8자 이상 15자 이하만 가능합니다.")
    @Pattern(regexp = "^[a-zA-Z_0-9]*$", message = "비밀번호는 알파벳 대소문자, 숫자만 사용 가능합니다.")
    private String password;

    private boolean admin = false;      // 디폴트 값은 false. 관리자 권한일 경우 true 로 변한다
    private String adminToken = "";
}

requestDto 에서 유효성 검사를 실시했다.
그러나 body 에는 유효성 검사 실패 메시지가 뜨지 않았는데, 이는 @RestControllerAdvice 를 이용한 전역 예외 처리가 있었기 때문이다.

service 단에서 유효성 검사 로직과 예외 메시지를 처리해봤으나, 정상적으로 반환되지 않았다.
유효성 검사에 관한 예외 처리 또한 @RestControllerAdvice 를 이용해야 했기 때문이다.

2. 해결법

GlobalExceptionHandler 는 ResponseEntityExceptionHandler 를 상속받는다.

ResponseEntityExceptionHandler 를 들어가 확인해보니, MethodArgumentNotValidException 클래스가 사용될 수 있다는 것을 확인 할 수 있었다.

GlobalExceptionHandler

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

	// 유효성 검사
    @ExceptionHandler(MethodArgumentNotValidException.class)
    protected ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();		// 1.
        StringBuilder stringBuilder = new StringBuilder();

        for (FieldError fieldError : bindingResult.getFieldErrors()) {		// 2.
            stringBuilder.append(fieldError.getDefaultMessage());		// 3.
            stringBuilder.append(" ");
        }
        final ErrorResponse response = new ErrorResponse(ErrorCode.INVALID_FORMAT.getHttpStatus().value(), String.valueOf(stringBuilder));	// 4.
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }

    ...
    
}

1.

  • @Valid 에 의한 MethodArgumentNotValidException 의 경우, getBindingResult 를 통해 에러 필드와 메세지를 추가해줄 수 있다.

2.

  • bindingResult.getFieldErrors()

    getFieldErrors() 가 FieldError 타입으로 List 에 담아서 반환한다는 것을 확인할 수 있다.
    그래서 for문을 돌면서 stringBuilder 에 추가해준다.

3.

  • 예외가 발생한 필드와 해당 예외가 발생했을 때 제공할 message 를 매핑시킨다.
    즉, SignupRequestDto 에서 username 과 password 각각에 설정해둔 에러 메시지와 매핑되는 것이다.

4.

  • ErrorCode.INVALID_FORMAT.getHttpStatus().value() 를 통해, INVALID_FORMAT 에러의 HTTP 상태 코드를 받을 수 있다.

    public enum ErrorCode {
        INVALID_FORMAT(HttpStatus.BAD_REQUEST, "올바르지 않은 유형"),
        ...
  • String.valueOf(stringBuilder) 를 통해, stringBuilder 에 담아둔 에러 메시지를 String 타입으로 변환한다.
    왜냐면 ErrorResponse 클래스에서는 ErrorCode 클래스에 선언된 String message 를 받아오기 때문이다.

3. 테스트


참고: [Java] Global Exception 이해하고 구성하기 : Controller Exception
참고: [Spring] @RestControllerAdvice를 이용한 Spring 예외 처리 방법 - (2/2)
참고: [예외처리] @Valid 예외처리에 사용되는 BindingResult 객체는 무엇일까?

profile
개발자로 거듭나기!

0개의 댓글