Spring Boot Validation
Validation이란 프로그래밍에 있어서 가장 필요한 부분으로 특히 java에서는 null 값에 대해서 접근하려고 할 때 NPE가 발생함으로, 이러한 부분을 방지하기 위해서 미리 검증을 하는 과정을 Validation이라고 함
제일 먼저 build.gradle에 다음을 추가함
implementation 'org.springframework.boot:spring-boot-starter-validation'
따라서 spring boot에는 일관적인 validator가 존재하고 이는 annotation기반으로 제공됨
@NotBlank
private String name;
@Min(value = 0, message = "사람의 나이는 양수입니다.")
private int age;
@Email
private String email;
@Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$")
private String phoneNumber;
유효성 검사를 해보는 코드이다. 그러나 해당 방법은 코드에서 예외처리하므로 logic관련 코드와 같이 있기 때문에 추천되는 방법은 아니다.
package com.example.spring.controller;
import com.example.spring.dto.ValidationUser;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequestMapping("/api")
public class ValidApiController {
// @Valid을 붙여서 유효성 검사하겠다!
// BindingResult는 코드에서 직접 예외처리하는 방법!
@PostMapping("/validation/user")
public ResponseEntity validationUser(@Valid @RequestBody ValidationUser validationUser,
BindingResult bindingResult){
// 유효성 검사에서 에러났을 때 해당 에러 출력하기
if(bindingResult.hasErrors()){
StringBuilder sb = new StringBuilder();
bindingResult.getAllErrors().forEach(objectError -> {
FieldError field = (FieldError) objectError;
String message = objectError.getDefaultMessage();
// 어디서 에러 났는지 key값 알아내기
System.out.println("field : " + field.getField());
// 유효성 에러 메세지 출력
System.out.println(message);
sb.append("field : "+field.getField());
sb.append("message : "+message);
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(sb.toString());
}
System.out.println(validationUser);
// 코드에서 예외처리하고 Logic 관련 코드가 실행되게함
return ResponseEntity.ok(validationUser);
}
}
// 그러나 한 클래스에 이렇게 만든다면 이런 형식이 필요한 곳에 재사용이 안됨됨
@AssertTrue(message = "yyyyMM의 형식에 맞지 않습니다.")
public boolean isReqYearMonthValidation(){
System.out.println("assert True call");
try{
LocalDate localDate = LocalDate.parse(this.reqYearMonth+"01", DateTimeFormatter.ofPattern("yyyyMMdd"));
}catch (Exception e){
return false;
}
return true;
}
package com.example.spring.annotation;
import com.example.spring.validator.YearMonthValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = {YearMonthValidator.class}) // YearMonthValidator 객체를 통해 검사가 이뤄짐
@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 { };
String pattern() default "yyyyMMdd";
}
package com.example.spring.validator;
import com.example.spring.annotation.YearMonth;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class YearMonthValidator implements ConstraintValidator<YearMonth, String> {
private String pattern;
@Override
public void initialize(YearMonth constraintAnnotation) {
this.pattern = constraintAnnotation.pattern();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// yyyyMMdd 체크
try{
// LocalDate 이기 때문에 "01"을 붙여야만 MM을 검색하게됨(default로 01일 이라고 붙임)
LocalDate localDate = LocalDate.parse(value+"01", DateTimeFormatter.ofPattern(this.pattern));
}catch (Exception e){
return false;
}
return true;
}
}
@YearMonth(pattern = "yyyyMMdd")
private String reqYearMonth;
사용과정이 조금 복잡하긴 하지만 재사용성이 높은 validator를 만드는 것이 좋다.
출처 : 한 번에 끝내는 Java/Spring 웹 개발 마스터 초격차 패키지 Online.