오답노트
[Spring] Validation 본문
Validation
1. 검증해야 할 값이 많은 경우 코드 길이가 길어진다.
2. 구현에 따라서 달라 질 수 있지만 Service Logic과 분리가 필요하다.
3. 흩어져 있는 경우 어디에서 검증을 하는지 알기 어려우며, 재사용의 한계가 있다.
4. 구현에 따라 달라 질 수 있지만, 검증 Logic이 변경되는 경우 테스트 코드 등 참조하는 클래스에서 Logic이 변경되어야 하는 부분이 발생 할 수 있다.
사용법
import com.example.validation.dto.User;
import jakarta.validation.Valid;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class ApiController {
@PostMapping("/user")
public Object user(@Valid @RequestBody User user, BindingResult bindingResult){
if(bindingResult.hasErrors()){
StringBuilder sb = new StringBuilder();
bindingResult.getAllErrors().forEach(objectError -> {
FieldError field = (FieldError) objectError;
String msg = objectError.getDefaultMessage();
System.out.println("feild : " + field.getField());
System.out.println("msg : " + msg);
sb.append("feild : " + field.getField());
sb.append("msg : " + msg);
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(sb.toString());
}
return user;
}
}
@Valid Annotation을 이용해 어떤 값을 검증할지 명시한다.
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
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 = "핸드폰 번호의 양식과 맞지 않습니다. 010-xxx(x)-xxxx")
private String phoneNumber;
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;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
", phoneNumber='" + phoneNumber + '\'' +
'}';
}
}
변수에 Annotation을 사용하여 어떤 Validation 방식으로 검증할지 명시한다.
각 Annotation 마다 사용법을 검색해서 사용하자. 그리고 각 Annotation은 message 옵션을 통해 출력 메시지를 변경할 수 있다.
Custom Validation
기존에 Spring에서 제공하는 Validation Annotation 외에 사용자가 직접 만들어서 사용할 수 있다.
import com.example.validation.validator.YearMonthValidator;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
@Constraint(validatedBy = {YearMonthValidator.class})
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.CONSTRUCTOR,ElementType.PARAMETER,ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface YearMonth {
String message() default "yyyyMMdd 의 형식에 맞지 않습니다.";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
String pattern() default "yyyyMMdd";
}
Annotation으로 사용할 인터페이스를 정의한다. 내용은 Spring에서 제공하던 Annotation을 참고하여 만든다.
pattern 변수는 입력될 패턴을 사용자가 Annotation 사용시 지정할 수 있다. 그리고 default 키워드를 이용해서 사용하지 않을 때, 기본적으로 사용할 패턴을 설정할 수 있다.
import com.example.validation.annotation.YearMonth;
import jakarta.validation.ConstraintValidator;
import jakarta.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) {
//yyyyMM
try {
LocalDate localDate = LocalDate.parse(value + "01", DateTimeFormatter.ofPattern(this.pattern));
}catch (Exception e){
return false;
}
return true;
}
}
인터페이스를 상속받아 내용을 구현한 클래스이다. ConstraintValidator를 상속 받아 가상 함수를 구현한다.
initalize에서는 인터페이스의 변수를 받아올 수 있고, isValid에서는 실제로 판별하는 로직을 구현하여 맞으면 true, 위배했다면 false를 return 시킨다.
'Java > Spring' 카테고리의 다른 글
[Spring] Exception과 Validation (0) | 2023.07.14 |
---|---|
[Spring] Exception (0) | 2023.07.14 |
[Spring] AOP (0) | 2023.07.13 |
[Spirng] IoC, DI (0) | 2023.07.13 |
[Spring Boot] Object Mapper (0) | 2023.07.13 |