spring-boot-web에 포함되어 있지만, 스프링 부트 2.3 버전 이후로 별도의 라이브러리로 spring-boot-starter-validation을 제공. <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Builder
public class ValidRequestDto {
@NotBlank
String name;
@Email
String email;
@Pattern(regexp="01(?:0|1|[6-9])[.-]?(\\d{3}|\\d{4})[.-]?(\\d{4})$")
String phoneNumber;
@Min(value=20) @Max(value = 40)
int age;
@Size(min=0, max=40)
String description;
@Positive
int count;
@AssertTrue
boolean booleanCheck;
}
문자열 검증
최댓값/최솟값 검증
값의 범위 검증
시간에 대한 검증
이메일 검증
자릿수 범위 검증
Boolean 검증
문자열 길이 검증
정규식 검증
@RestController
@RequestMapping("/validation")
public class ValidationController {
private final Logger LOGGER = LoggerFactory.getLogger(ValidationController.class);
@PostMapping("/valid")
public ResponseEntity<String> checkValidationByValid(
@Valid @RequestBody ValidRequestDto validRequestDto){
LOGGER.info(validRequestDto.toString());
return ResponseEntity.status(HttpStatus.OK).body(validRequestDto.toString());
}
}



오류메시지
[Field error in object 'validRequestDto' on field 'age': rejected value [19];
codes [Min.validRequestDto.age,Min.age,Min.int,Min]; arguments
[org.springframework.context.support.DefaultMessageSourceResolvable: codes
[validRequestDto.age,age]; arguments []; default message [age],20];
default message [20 이상이어야 합니다]] ]
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Builder
public class ValidatedRequestDto {
@NotBlank
private String name;
@Email
private String email;
@Pattern(regexp = "01(?:0|1|[6-9][.-]?(\\d{3}|\\d{4})[.-]?(\\d{4})$")
private String phoneNumber;
@Min(value=20, groups = ValidationGroup1.class)
@Max(value=40, groups = ValidationGroup1.class)
private int age;
@Size(min=0, max=40)
private String description;
@Positive(groups= ValidationGroup2.class)
private int count;
@AssertTrue
private boolean booleanCheck;
}

@RestController
@RequestMapping("/validation")
public class ValidationController {
private final Logger LOGGER = LoggerFactory.getLogger(ValidationController.class);
@PostMapping("/valid")
public ResponseEntity<String> checkValidationByValid(
@Valid @RequestBody ValidRequestDto validRequestDto){
LOGGER.info(validRequestDto.toString());
return ResponseEntity.status(HttpStatus.OK).body(validRequestDto.toString());
}
@PostMapping("/validated")
public ResponseEntity<String> checkValidation(
@Validated @RequestBody ValidatedRequestDto validatedRequestDto){
LOGGER.info(validatedRequestDto.toString());
return ResponseEntity.status(HttpStatus.OK).body(validatedRequestDto.toString());
}
@PostMapping("/validated/group1")
public ResponseEntity<String> checkValidation1(
@Validated(ValidationGroup1.class) @RequestBody ValidatedRequestDto validatedRequestDto){
LOGGER.info(validatedRequestDto.toString());
return ResponseEntity.status(HttpStatus.OK).body(validatedRequestDto.toString());
}
@PostMapping("/validated/group2")
public ResponseEntity<String> checkValidation2(
@Validated(ValidationGroup2.class) @RequestBody ValidatedRequestDto validatedRequestDto){
LOGGER.info(validatedRequestDto.toString());
return ResponseEntity.status(HttpStatus.OK).body(validatedRequestDto.toString());
}
@PostMapping("/validated/all-group")
public ResponseEntity<String> checkValidation3(
@Validated({ValidationGroup2.class, ValidationGroup1.class}) @RequestBody ValidatedRequestDto validatedRequestDto){
LOGGER.info(validatedRequestDto.toString());
return ResponseEntity.status(HttpStatus.OK).body(validatedRequestDto.toString());
}
}
public class TelephoneValidator implements ConstraintValidator<Telephone,String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if(value == null){
return false;
}
return value.matches("01(?:0|1|[6-9])[.-]?(\\d{3}|\\d{4})[.-]?(\\d{4})$");
}
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = TelephoneValidator.class)
public @interface Telephone {
String message() default "전화번호 형식이 일치하지 않습니다.";
Class[] groups() default {};
Class[] payload() default {};
}