@ModelAttribute로 지정된 모델 오브젝트의 바인딩 작업이 실패로 끝나는 경우는 두 가지가 있다.
- 타입 변환이 불가한 경우
- 타입 변환은 성공했지만 검증기(Validator)를 이용한 검사가 통과하지 못했을 때
Validator를 통한 검증 과정의 결과는 BindingResult를 통해 확인할 수 있다. BindingResult는 Errors의 서브인터페이스다.
Validator
스프링에서 범용적으로 사용할 수 있는 오브젝트 검증기를 정의할 수 있는 API
@Controller로 HTTP요청을 @ModelAttribute 모델에 바인딩 할 때 주로 사용된다. 또한 비즈니스 로직에서 검증 로직을 분리하고 싶을 때도 사용할 수 있다.
public interface Validator{
boolean support(Class<?> clazz); // 검증기가 검증할 수 있는 오브젝트 타입인지 확인해주는 메소드
void validate(Object target, Errors erros); // supports() 통과한 경우에만 validate가 호출된다.
}
Validator를 이용한 검증 중에 오류가 발생하면 Errors 인터페이스를 통해서 특정 필드나 모델 오브젝트 전체에 대해 오류정보를 등록할 수 있다. Errors 인터페이스를 통해서 등록된 오류는 최종적으로 BindingResult에 담겨 컨트롤러에 전달된다. 검증 결과를 보고 컨트롤러는 그에 맞는 처리를 하고 뷰를 선택한다.
사용자 입력 폼을 처리하는 컨트롤러 메소드에서 사용할 UserValidator를 만들어서 사용해보자.
@Service
public class UserValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) { // 타입 검증
// 서브클래스가 사용될 수 있도록 isAssignableForm 사용
return User.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) { // 입력값 검증
User user = (User) target;
if(user.getUsername() == null || user.getUsername().length() == 0){
// 두 번째 인자는 에러 코드
errors.rejectValue("username", "field.required");
}
// 위의 코드와 같다.
ValidationUtils.rejectIfEmpty(errors, "username", "field.required");
}
2가지 사용방법이 존재한다.
1. 컨트롤러 메소드 내의코드
Validator를 빈으로 등록 후 컨트롤러에서 DI받아서 사용한다.
@Controller
public class SimpleController {
@Autowired
private UserValidator validator;
@GetMapping("/add")
public void add(@ModelAttribute User user, BindingResult result){
log.info("User : {}", user);
this.validator.validate(user, result);
if(result.hasErrors()){
log.info("오류가 발견되었을 때..");
System.out.println(result.getAllErrors());
}else{
log.info("오류가 없을 때..");
}
}
}
validate() 메소드를 실행한 후에 BindingResult의 hasErrors() 메소드로 검증 작업에서 등록된 오류가 있는지 확인한다.
2. @Valid를 이용한 자동검증
JSR-303의 @javax.validation.Valid 어노테이션을 사용한다. 스프링이 이 @Valid라는 애노테이션을 차용했을 뿐 내부적으로는 스프링의 Validator를 이용한 검증이 수행된다.
⇒ WebDataBinder에 등록해서 사용한다. (WebDataBinder는 프로퍼티 에디터, 컨버전 서비스 뿐만 아니라 Validator 타입의 검증용 오브젝트도 설정할 수 있다.)
@Controller
public class SimpleController {
@Autowired
private UserValidator validator;
@InitBinder
public void initBinder(WebDataBinder webDataBinder){
webDataBinder.setValidator(this.validator);
}
@GetMapping("/add")
public void add(@ModelAttribute @Valid User user, BindingResult result){
...
}
}
모델을 바인딩하는 WebDataBinder는 @InitBinder 메소드에서 등록된 Validator를 이용해 모델을 검증하고, 그 결과를 BindingResult에 넣어서 전달해 준다. 따라서 메소드의 파라미터로 전달받는 BindingResult에는 이미 UserValidator로 검증한 결과가 담겨 있다.
JSR-303 빈 검증 기능
스프링에서는 LocalValidatorFcatoryBean을 이용해 JSR-303의 검증 기능을 사용할 수 있다.
LocalValidatorFactoryBean은 JSR-303의 검증 기능을 스프링의 Validator처럼 사용할 수 있게 해주는 일종의 어댑터이다.
JSR-303의 빈 검증 기술의 특징은 모델 오브젝트의 필드에 달린 제약조건 애노테이션을 이용해 검증을 진행할 수 있다는 점이다.
@Getter
@Setter
public class User{
@NotNull
private String username;
@Min(0)
private int age;
}
이 제약조건 애노테이션을 사용해 검증을 수행하려면 LocalValidatorFactoryBean을 빈으로 등록해줘야 한다. (이 팩토리 빈이 생성하는 빈의 타입은 스프링 Validator이다.)
<bean id="localValidator" class="org.springframework.beanvalidation.LocalValidatorFactoryBean"/>
컨트롤러에 DI 받아서 사용하면 되며, 아래의 두 가지 방법 중 하나를 선택해서 사용하면 된다.
- @InitBinder에서 WebDataBinder에 넣고 @Valid를 이용해 자동검증
- 코드 내에서 직접 validate() 메소드를 호출해 사용
'프로그래밍 노트 > SPRING' 카테고리의 다른 글
[Spring] MessageSource (0) | 2020.03.16 |
---|---|
[Spring] Environment 프로파일/프로퍼티 (0) | 2020.03.11 |
[Spring] 모델 바인딩과 검증_2(Converter) (0) | 2019.12.26 |
[Spring] 모델 바인딩과 검증_1 (0) | 2019.12.23 |
[Spring] @ModelAttribute, @RequestAttribute (5) | 2019.07.17 |