시작하기 전에 JSR-303 Validator(Validator 인터페이스의 구현체)를 적용하기 위해서는 아래의 dependency를 pom.xml에 추가해야 한다.
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
</dependency>
Validator 인터페이스는 두가지 메서드를 정의하는데, supports메서드와 validate메서드이다.
supports메서드는 검증하고자 하는 객체가 validator에서 지원하는지 아닌지 확인하는 메서드이고, validate메서드는 실제 로직을 구현하는 메서드이다.
아무튼 코드로 Validator 인터페이스를 상속받아서 구현하자면,
public class EventValidator implements Validator {
@Override
public boolean supports(Class<?> aClass) {
//해당 객체가 validator가 지원하는 객체인지 판단하기 위해서 override하는 메서드
return Event.class.equals(aClass);
}
@Override
public void validate(Object target, Errors errors) {
//ValidationUtils를 사용하여 validate을 구현하거나
//rejectIfEmptyOrWhitespace 함수의 경우 해당 필드가 비어있거나 공백일 경우 발생시킴
//1번인자 Error객체, 2번인자 필드이름, 3번인자 에러코드, 4번인자 디폴트 메세지
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "title", "NOT_EMPTY", "DEFALT_MESSAGE");
//또는 직접 Error 객체에 접근해서 validate을 구현할 수도 있음
Event event = (Event) target;
if(event.getClass()==null){
//객체 전반의 에러라면 reject 메서드
errors.reject("Error Message");
//특정 필드의 에어라면 rejectValue 메서드
//첫번째 인자가 필드 이름, 두번째 인가자 에러코드드
errors.rejectValue("fieldName", "ErrorMessage");
}
}
}
그리고 검증받을 객체는,,,
public class Event {
private Integer id;
private String title;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
Validator를 상속받은 클래스를 생성하고 validate매서드를 실행시키는 것임.
validate메서드는 검증할 객체와 Errors객체를 받아서 검증처리를 하고 Errors객체의 메서드를 통해서 e.g. errors.hasErrors() 또는 errors.getAllErrors()등의 메서드를 활용해서 검증 내용을 확인함.
직관적을 코드로 보자면,,
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
Validator validator;
@Override
public void run(ApplicationArguments args) throws Exception {
Event event = new Event();
EventValidator eventValidator = new EventValidator();
//Errors는 인터페이스이고 이를 구현한 구현체는 BeanPropertyBindingResult
// 파라미터로 검증할 객체와 객체의 이름을 String으로 넘김
Errors errors = new BeanPropertyBindingResult(event, "event");
eventValidator.validate(event, errors);
System.out.println(errors.hasErrors());
errors.getAllErrors().forEach(error->{
System.out.println("----error code------");
Arrays.stream(error.getCodes()).forEach(System.out::println);
System.out.println(error.getDefaultMessage());
});
}
}
그리고 검증결과는...
이처럼 실행되기 때문에
if(erros.hasError){
//예외처리 내용
}
등으로 예외처리를 하면 된답
Validator인터페이스를 상속받아 직접 validator를 구현해도 되지만, 갓갓 스프링은 이미 validator인터페이스를 구현해 놓은 LocalValidatorFactoryBean 클래스를 빈으로 등록해 놓았기 때문에
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
Validator validator;
@Override
public void run(ApplicationArguments args) throws Exception {
Event event = new Event();
//Errors는 인터페이스이고 이를 구현한 구현체는 BeanPropertyBindingResult
// 파라미터로 검증할 객체와 객체의 이름을 String으로 넘김
Errors errors = new BeanPropertyBindingResult(event, "event");
eventValidator.validate(event, errors);
}
}
이렇게 빈으로 주입받아서 사용할 수 있도록 되어 있다.
Validator 인터페이스를 직접 구현해서 사용할 때처럼 validate.validate()메서드에 검증할 객체와 Errors객체를 넣고 errro.hasErorr()등의 메서드로 확인함.
그런데 말입니다? 그럼 무엇을 어떻게 검증할건지 어떻게 아나요?
정답은 검증받을 객체의 필드에 여러가지 어노테이션을 필드에 붙여서 확인하게 된다.
예를들면...
public class Event {
@Min(1) //최소값이 1이라는거, 즉, 0이나 음수를 가질 수 없음
private Integer id;
@NotEmpty //Null이나 ""빈 물자열을 가질 수 없음
private String title;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
이제 검증할 객체의 필드에 붙이는 어노테이션들을 하나하나 아라보자!