[SpringBoot] Custom Validation Exception 처리

RUNGOAT·2023년 5월 29일
0

SpringBoot

목록 보기
3/3

🔑 Spring Validation이란?

💡 요청을 통해 들어오는 form 객체 또는 Dto에 대해 값의 유효성 검사(validation)을 진행하여 올바르지 않은 데이터가 서버로 전송되거나, DB에 저장되지 않도록 한다.

  • 여기서는 예외 처리에 대해 다룰 것이기 때문에 사용법에 대해서는 언급하지 않겠다.

📢 예외 처리 Code 구현

1️⃣   CustomExceptionHandler

💡 Controller 전역에서 발생하는 Custom Error를 잡아줄 Handler를 생성한다.

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class CustomExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    ResponseEntity<ErrorResponseEntity> handleValidationException(MethodArgumentNotValidException e) {
        return ErrorResponseEntity.toResponseEntity(e);
    }
}

@ControllerAdvice

  • 모든 @Controller 즉, 전역에서 발생할 수 있는 예외를 잡아 처리한다.

@ExceptionHandler(MethodArgumentNotValidException.class)

  • Validation 관련 예외는 MethodArgumentNotValidException을 발생시킨다.
    해당 예외가 발생하면 handleValidationException 메소드에서 공통 처리한다.

@ControllerAdvice + @ExceptionHandler

  • 모든 컨트롤러에서 발생하는 MethodArgumentNotValidException catch한다.

2️⃣   ErrorResponseEntity

💡 Validation Error 내용을 담을 Response Entity를 생성한다.

@Data
@Builder
public class ErrorResponseEntity {

    private String code;
    private String message;
    
    public static ResponseEntity<ErrorResponseEntity> toResponseEntity(MethodArgumentNotValidException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        FieldError fieldError = fieldErrors.get(fieldErrors.size()-1);  // 가장 첫 번째 에러 필드
        String fieldName = fieldError.getField();   // 필드명
        Object rejectedValue = fieldError.getRejectedValue();   // 입력값

        return ResponseEntity
                .status(HttpStatus.BAD_REQUEST)
                .body(ErrorResponseEntity.builder()
                        // 에러 코드 in 에러 코드 명세서
                        .code(fieldError.getDefaultMessage())
                        .message(fieldName + " 필드의 입력값[ " + rejectedValue + " ]이 유효하지 않습니다.")
                        .build());
    }
}

Response 결과 예시

{
    "code": "{ErrorCode}",
    "message": "{fieldName} 필드의 입력값[ null ]이 유효하지 않습니다. "
}


📢 사용

Request Dto 생성

public class TestReqDto {

    // message에는 에러 코드 명세서에 선언한 에러 코드를 대입한다.
    @NotNull(message = "ERROR-001")
    private String title;

}

message 값으로는 " "(문자열)만 입력 가능하다.


Controller에서 유효성 검사

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/test")
public class TestController {

	private final TestService testService;

    @PostMapping
    public ResponseEntity<HttpStatus> test(@Valid @RequestBody TestReqDto dto) {
    	testService.test(dto);
        return ResponseEntity.ok();
    }

}
  • @Valid annotation을 사용해 입력값의 유효성 검사를 진행한다.

  • 만약 title의 값이 없어 null이 들어온다면 MethodArgumentNotValidException를 발생시키고 해당 Error를 위의 코드로 처리하게 된다.

  • 결과값
    {
        "code": "ERROR-001",
        "message": "title 필드의 입력값[ null ]이 유효하지 않습니다. "
    }


주의

message 값은 문자열만 가능

@Getter
@AllArgsConstructor
public enum Error {

    TEST_ERROR("ERROR-001");

    private final String message;

}
// @NotNull(message = Error.TEST_ERROR.getMessage())
// -> Attribute value must be constant
private String title;
  • 이렇게 Enum을 사용해서 String을 대입하는 건 불가능하다.
  • 반드시 상수이어야 하기 때문에 " "(문자열)을 입력해야 한다.

final String code = "ERROR-001";

@NotNull(message = code)
private String title;
  • 하지만 final을 사용해 문자열을 담은 변수는 입력 가능하다.


@NotNull을 적용하는 필드의 데이터는 참조 타입

  • 원시 타입의 경우(int, double 등) 기본값이 0이기 때문에 데이터가 없는 경우(null인 경우)가 존재하지 않는다.
  • 참조 타입(Integer, Double)을 사용해야 데이터가 없을 때(null인 경우) 예외를 발생시킨다.
profile
📞피드백 너무나 환영

0개의 댓글