Spring Boot Validation
Validation
: Java에서는 NULL값에 대한 접근하려고 할 때 Null Pointer Exception 이 발생, 이러한 부분을 방지 하기 위해 미리 검증하는 과정을 validation
Spring 에서 제공하는 Validation 적용
1. gradle dependecies
implementation ‘org.springframework.boot:spring-boot-starter-validation’
2. bean validation 정의
https://beanvalidation.org/2.0-jsr380
Validation 어노테이션
@Size | 문자 길이 측정 |
@NotNull | null 불가 |
@NotEmpty | null, “” 불가 |
@NotBlank | null,””, “ “ 불가 |
@Past | 과거 날짜 |
@PastOrPresent | 오늘이거나 과거날짜 |
@Future | 미래 날짜 |
@FutureOrPresent | 오늘이거나 미래 날짜 |
@Pattern | 정규식 |
@Max | 최대값 |
@Min | 최소값 |
@AssertTre/False | 별도 로직 적용 |
@Valid | 해당 object validation |
1. Spring Boot Validation 사용
* 중요
* @AssertTrue
* 메소드 명에 is~ 명칭 사용
* @AssertTrue를 사용하여 DTO에 별도의 valid 로직을 적용
* 단, 재사용이 불가
User.java
public class User {
@NotBlank
private String name;
@Max(value = 90)
private int age;
@Email
private String email;
// 정규식 사용
@Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$",message = "핸드폰 번호의 양식과 맞지 않습니다.")
private String phoneNumber;
@Size(min = 6, max = 6) // yyyyMM
//@YearMonth
private String reqYearMonth;
@Valid
private List<CarDto> cars;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getReqYearMonth() {
return reqYearMonth;
}
public void setReqYearMonth(String reqYearMonth) {
this.reqYearMonth = reqYearMonth;
}
public List<CarDto> getCars() {
return cars;
}
public void setCars(List<CarDto> cars) {
this.cars = cars;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
", phoneNumber='" + phoneNumber + '\'' +
", reqYearMonth='" + reqYearMonth + '\'' +
", cars=" + cars +
'}';
}
/*
* 메소드 명에 is~ 명칭 사용
* @AssertTrue를 사용하여 DTO에 별도의 valid 로직을 적용
* 단, 재사용이 불가
*/
@AssertTrue(message = "yyyyMM의 형식에 맞지 않습니다.")
public boolean isReqYearMonthValidation(){
try{
LocalDate localDate = LocalDate.parse(getReqYearMonth()+"01", DateTimeFormatter.ofPattern("yyyyMMdd"));
System.out.println(localDate);
}catch(Exception e){
return false;
}
return true;
}
}
* 중요
* @Valid - RequestBody의 객체에 valid 어노테이션이 있을 경우 @valid적용
* BindingResult - @Valid의 결과값이 들어옴
* FieldError - 필드 오류, 즉 특정 필드 값을 거부하는 이유를 캡슐화합니다.필드의 이름, 코드, 메세지, 싶패한값/getField(),getCode(),getDefaultMessage(),getRejectedValue(),
ApiController.java
* Validation 사용하여 Http응답 Error 확인
@RestController
@RequestMapping("/api")
public class ApiController {
/*
* @Valid - RequestBody의 객체에 valid 어노테이션이 있을 경우 @valid적용
* BindingResult - @Valid의 결과값이 들어옴
* FieldError - 필드 오류, 즉 특정 필드 값을 거부하는 이유를 캡슐화합니다.필드의 이름, 코드, 메세지, 싶패한값/getField(),getCode(),getDefaultMessage(),getRejectedValue(),
*/
@PostMapping("/user")
public ResponseEntity user(@Valid @RequestBody User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
System.out.println("Erros : ");
StringBuilder st = new StringBuilder();
bindingResult.getAllErrors().forEach(objectError -> {
FieldError field = (FieldError) objectError;
String msg = field.getDefaultMessage();
//System.out.println("field : " + field.getField());
//System.out.println(msg);
st.append("field : " + field.getField());
st.append("\nmessage : " + msg+"\n");
});
System.out.println(st.toString());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(st.toString());
}
System.out.println("OK : ");
System.out.println(user);
return ResponseEntity.ok(user);
}
}
HTTP 요청 BODY
{
"name":"홍길동",
"age":90,
"email":"honggil@gmail.com",
"phoneNumber":"010-1234-5678",
"reqYearMonth":"2022ㅁ0",
"cars": [
{
"name": "AUDI",
"carNum": "11가1111",
"type": "4WD"
},
{
"name": "BMW",
"carNum": "22나2222",
"type": "2WD"
}
]
}
HTTP 응답 BODY
field : reqYearMonthValidation
message : yyyyMM의 형식에 맞지 않습니다.
콘솔 결과
Erros :
field : reqYearMonthValidation
message : yyyyMM의 형식에 맞지 않습니다.
2. Spring Boot Custom Validation 사용
User.java 수정
//@Size(min = 6, max = 6) // yyyyMM
@YearMonth // 커스텀 어노테이션
private String reqYearMonth;
* 중요
@Constraint(validatedBy = {YearMonthValidator.class}) // 해당 validatior 클래스 지정
YearMonth.java - 커스텀 어노테이션 인터페이스 생성
@Constraint(validatedBy = {YearMonthValidator.class}) // 해당 validatior 클래스 지정
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface YearMonth {
String message() default "yyyyMM 형식에 맞지 않습니다.";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
/* 위의 변수나 어노테이션은 기존 스프링 valid 어노테이션에서 가져옴 */
String pattern() default "yyyyMMdd";
}
YearMonthValidator.java - 어노테이션 Valid 적용시키기 위한 validator 클래스
public class YearMonthValidator implements ConstraintValidator<YearMonth, String> {
private String pattern;
/*
* 해당 커스텀 어노테이션 값 초기화
*/
@Override
public void initialize(YearMonth constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
this.pattern = constraintAnnotation.pattern();
}
/*
* 커스텀 어노테이션 validation 로직
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
try{
LocalDate localDate = LocalDate.parse(value+"01", DateTimeFormatter.ofPattern(this.pattern));
System.out.println(localDate);
}catch(Exception e){
return false;
}
return true;
}
}
HTTP 요청 BODY
{
"name":"홍길동",
"age":90,
"email":"honggil@gmail.com",
"phoneNumber":"010-1234-5678",
"reqYearMonth":"2022ㅁ0",
"cars": [
HTTP 응답 BODY
field : reqYearMonth
message : yyyyMM 형식에 맞지 않습니다.
콘솔 결과
Erros :
field : reqYearMonth
message : yyyyMM 형식에 맞지 않습니다.
3. DTO내 DTO의 validation 적용
CarDto.java
public class CarDto {
@NotBlank
private String name;
@NotBlank
@JsonProperty("carNum")
private String carNum;
@NotBlank
@JsonProperty("type")
private String type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCarNum() {
return carNum;
}
public void setCarNum(String carNum) {
this.carNum = carNum;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "CarDto{" +
"name='" + name + '\'' +
", carNum='" + carNum + '\'' +
", type='" + type + '\'' +
'}';
}
}
User.java - settter/getter/toString포함 추가
* @Valid 어노테이션을 사용하여 해당 dto의 validation 어노테이션 적용
@Valid
private List<CarDto> cars;
HTTP 요청 BODY
{
"name":"홍길동",
"age":90,
"email":"honggil@gmail.com",
"phoneNumber":"010-1234-5678",
"reqYearMonth":"202210",
"cars": [
{
"name": "AUDI",
"carNum": "",
"type": "4WD"
},
{
"name": "BMW",
"carNum": "22나2222",
"type": "2WD"
}
]
}
HTTP 응답 BODY
field : cars[0].carNum
message : 공백일 수 없습니다
콘솔 결과
Erros :
field : cars[0].carNum
message : 공백일 수 없습니다
'Spring > 98. Infra' 카테고리의 다른 글
4. Spring Filter와 Interceptor (0) | 2022.11.14 |
---|---|
3. Spring Boot Validation과 Exception 예제 (0) | 2022.11.07 |
2. Spring Boot Exception (0) | 2022.11.07 |