서버는 시스템의 보안, 무결성, 안정성을 위해 클라이언트가 보낸 데이터가 유효한지 항상 검사해야 합니다. 악의적인 클라이언트가 조작된 데이터를 서버에 보낼 수도 있고, 수신된 데이터가 데이터베이스에 저장되는 형식, 비즈니스 로직에서 사용되는 형식과 다르면 문제가 될 수 있기 때문입니다. 이는 곧 사용자 경험을 떨어뜨리는 결과로 이어질 수 있습니다.
Java에서 많이 사용되는 validator로는 사실상 표준인 Hibernate Validator가 있습니다. Hibernate Validator는 annotation 기반으로 데이터 유효성을 검사하며, 다양한 프레임워크에서 지원됩니다.
Hibernate Validator api docs에 들어가면 Hibernate Validator api를 확인할 수 있습니다.
time, money, number, size, empty 등에 대한 유효성을 검사하는 패키지들이 있고, org.hibernate.validator.constraints 패키지에 많이 사용되는 유효성 검사 어노테이션이 있습니다.
이 글은 Hibernate Validator의 간단한 사용법과 spring boot에 적용하는 방법을 설명합니다.
public class User {
@NotNull
@Size(min = 2, max = 50)
private String name;
@NotNull
@Email
private String email;
// Getters and setters
}
위 코드에서 보듯 클래스 필드에 annotation을 사용하여 유효성 검사를 수행할 수 있습니다.
많이 사용되는 어노테이션은 다음과 같습니다.
| Annotation | 설명 | 주요 속성 |
|---|---|---|
| @NotNull | 필드가 null이 아닌지 확인합니다. | |
| @Size | 문자열, 컬렉션 또는 배열의 크기를 확인합니다. | min, max, message |
| @Pattern | 값이 지정된 정규식과 일치하는지 확인합니다. | regexp, message |
| 값이 올바른 형식의 이메일 주소인지 확인합니다. | ||
| @Min | 숫자 값이 지정된 최소값보다 크거나 같은지 확인합니다. | value, message |
| @Max | 숫자 값이 지정된 최대값보다 작거나 같은지 확인합니다. | value, message |
| @NotEmpty | 문자열, 컬렉션 또는 배열이 비어 있지 않은지 확인합니다. | |
| @NotBlank | 문자열이 공백이 아니라 비어 있지 않은지 확인합니다. |
참고) message 속성은 유효성 검증에 실패했을 때 확인할 수 있는 문자열입니다.
implementation 'org.springframework.boot:spring-boot-starter-validation'
validator annotation으로 유효성 규칙을 설정합니다.public class PostRequest {
@Size(max = 100)
@NotBlank
private String title;
@NotBlank
private String content;
@NotEmpty
private String category;
}
@Valid와 @RequestBody을 함께 사용하여 수신 데이터의 유효성을 검사합니다.public ResponseEntity<PostResponse> writePost(@RequestBody @Valid PostRequest req)
throws IOException{
...
}
@RequestBody
spring은 HTTP 요청의 body를 @RequestBody가 붙은 Controller method의 매개변수로 변환합니다.
@Valid
@RequestMethod 실행 후 spring은 @Valid를 감지하고 Hibernate Validator를 사용하여 해당 객체의 유효성을 검사합니다.
참고) 클래스 레벨에서 유효성을 검증하는 @Validated 어노테이션도 있습니다.
Spring Boot는 @ControllerAdvice와 @ExceptionHandler 로 Controller 단의 Exception을 전역적으로 처리할 수 있습니다.
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.FieldError;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors()
.forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return ResponseEntity.badRequest().body(errors);
}
}
유효성 검사 실패 시 유효성 검사에 실패한 모든 필드와 결과 메세지를 Map에 담고, badRequest 코드와 함께 응답하는 코드입니다.
@RestControllerAdvice: @ControllerAdvice 및 @ResponseBody 를 합친 어노테이션입니다.
@ExceptionHandler: 속성으로 특정 예외를 설정하여 해당 예외가 발생한 경우, @ExceptionHandler가 붙은 메서드를 실행하여 응답합니다.
MethodArgumentNotValidException: @Valid가 붙은 argument의 유효성 검사가 실패할 때 발생하는 예외입니다.
getBindingResult(): 유효성 검사에 실패한 필드와 관련 메시지 등을 확인할 수 있는 BindingResult를 가져올 수 있습니다.
좋은 글이네요. 공유해주셔서 감사합니다.