개발 중 컨트롤러에서 @RequestBody 객체를 검증할 때 @Valid 어노테이션을 사용해서 요청 객체에 대한 유효성 검증을 할 수 있다. 요청 객체에 대한 유효성 검증을 어떻게 하는지 차근차근 알아보도록 하자.
validation 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-validation'
build.gradle에 먼저 spring-boot-starter-validation 의존성을 추가한다.
@Valid 적용 (컨트롤러, 요청 DTO)
@PostMapping("/join")
public ResponseEntity<Object> joinMember(@Valid @RequestBody MemberJoinDto memberJoinDto) {
return ResponseEntity.ok(memberService.joinMember(memberJoinDto));
}
컨트롤러에서 멤버등록 메소드의 @RequestBody인 MemberJoinDto에 @Valid 어노테이션을 붙여준다.
@Getter
public class MemberJoinDto {
@NotBlank(message = "아이디는 공백일 수 없습니다.")
private String userId;
@NotBlank(message = "이름은 공백일 수 없습니다.")
private String userName;
@NotBlank(message = "비밀번호는 공백일 수 없습니다.")
private String password;
@NotBlank(message = "휴대폰번호는 공백일 수 없습니다.")
private String phoneNumber;
}
MemberJoinDto의 필드들에 대해 Valid의 Constraints 중 하나인 @NotBlank를 적용하고 필드가 없을 때의 메세지를 정의한다.
여기까지 컨트롤러에서 @Valid 어노테이션과 DTO에 Contraints 어노테이션을 적용해 주면 유효성 검증 끝이다.
Valid Contraints
[문자열 검증]
@Null : null만 허용
@NotNull : null을 허용하지 않고, "", " "는 허용
@NotEmpty : null, ""을 허용하지 않고, " "는 허용
@NotBlank : null, "", " " 모두 허용하지 않음
[최대 최소 검증]
@DecimalMax : 지정된 최대 값보다 작거나 같아야 함 (Require : String value => max 값을 지정)
@DecimalMin : 지정된 최소 값보다 크거나 같아야 함 (Require : String value => min 값을 지정)
@Max : 지정된 최대 값보다 작거나 같아야 함 (Require : int value => max 값을 지정)
@Min : 지정된 최소 값보다 크거나 같아야 함 (Require : int value => min 값을 지정)
[범위 값에 대한 검증]
@Positive : 양수인 값
@PositiveOrZero : 0이거나 양수인 값
@Negative : 음수인 값
@NegativeOrZero : 0이거나 음수인 값
[시간에 대한 검증]
@Future : Now 보다 미래의 날짜, 시간
@FutureOrPresent : Now 이거나 미래의 날짜, 시간
@Past : Now 보다 과거 의의 날짜, 시간
@PastOrPresent : Now 이거나 과거의 날짜, 시간
[자릿수검증]
@Digits : 자릿수 검증 (ex. @Digits(integer = 5, fraction = 5))
[크기 검증]
@Size(min=, max=) : 길이 제한
[이메일 검증]
@Email : 이메일 형식을 검사 (""의 경우 통과되기 때문에 @Patten을 통한 정규식 검사를 더 많이 사용)
[정규식 검증]
@Pattern(regexp = ) : 정규식 검사
[Boolean값 검증]
@AssertFalse : false 여부 (null은 체크하지 않음)
@AssertTrue : true 여부 (null은 체크하지 않음)
예외 처리
@Valid 어노테이션을 컨트롤러에 적용했다면, 유효성 검증에 실패한 경우 발생하는 예외를 처리해 보도록 하자.
유효성 검증에 실패하는 경우 발생하는 예외는 MethodArgumentNotValidException이다.
@Slf4j
@RestControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {
...
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
String errorMessage = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
return new ResponseEntity<>(new ErrorResponse(String.valueOf(ex.getStatusCode().value()), errorMessage), ex.getStatusCode());
}
...
}
내 프로젝트의 경우 커스텀 예외처리 핸들러에서 ResponseEntityExceptionHandler를 상속받기 때문에 해당 예외를 오버라이드해서 처리했다. MethodArgumentNotValidException 예외 발생 시 DefaultMessage(DTO에서 constraints에 설정했던 message)를 ErrorResponse에 담아준다.
{
"result": false,
"code": "400",
"message": "비밀번호는 공백일 수 없습니다."
}
멤버등록 메소드에 password 필드를 넣지 않으면 위와 같은 에러를 반환한다.
@Slf4j
@RestControllerAdvice
public class CustomExceptionHandler {
...
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
String errorMessage = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
return new ResponseEntity<>(new ErrorResponse(String.valueOf(ex.getStatusCode().value()), errorMessage), ex.getStatusCode());
}
...
}
만약 본인의 프로젝트의 예외처리 핸들러가 ResponseEntityExceptionHandler를 상속받고 있지 않다면 @ExceptionHandler 어노테이션으로 해당 예외를 받아서 처리하면 된다.
ResponseEntityExceptionHandler를 상속받는데 @ExceptionHandler를 통해 예외를 받게 되면 이미 해당 예외를 처리하고 있기 때문에 "Ambiguous @ExceptionHandler method mapped for ..." 와 같은 에러 메세지를 만날 것이다.
-끝-
'Spring Boot' 카테고리의 다른 글
[Spring Boot] 로그인 성공 시 쿠키에 accessToken 넣기 (0) | 2023.12.25 |
---|---|
[SpringBoot] @Secured, @PreAuthorize, @PostAuthorize (0) | 2023.12.24 |
[Spring Boot] 공통 에러 처리 (0) | 2023.11.26 |