컨트롤러의 매개변수를 받는 곳에서 유효성 검증을 하나하나 진행하는 것이 아니라, 데이터 유효성 검증에 관한 부분을 validator를 생성하고 호출하여 검증을 진행하도록 한다.
1. Validator를 상속받는 UserValidator 클래스 생성
public class UserValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
// return User.class.equals(clazz); // 검증하려는 객체가 User타입인지 확인
return User.class.isAssignableFrom(clazz); // clazz가 User 또는 그 자손인지 확인
}
@Override
public void validate(Object target, Errors errors) {
System.out.println("LocalValidator.validate() is called");
User user = (User)target;
String id = user.getId();
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "pwd", "required");
if(id==null || id.length() < 5 || id.length() > 12) {
errors.rejectValue("id", "invalidLength");
}
}
}
2. 로컬 Validator : UserValidator 클래스를 사용하는 두 가지 방법
2-1. 수동 검증 - 매개변수를 받는 메서드 내에서 직접 호출한다.
@Controller
public class RegisterController {
@InitBinder
public void toDate(WebDataBinder binder) {
SimpleDateFormat df = new SimpleDateFormat("yyyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(df, false)); //타입으로 지정
binder.registerCustomEditor(String[].class, "hobby", new StringArrayPropertyEditor("#")); //타입과 이름으로 지정
}
@PostMapping("/register/save")
public String save(User user, BindingResult result, Model m) throws UnsupportedEncodingException {
// 수동 검증 - Validator를 직접 생성하고, validator()를 직접 호출
UserValidator userValidator = new UserValidator();
userValidator.validate(user, result); //BindingResult는 Errors의 자손
if(result.hasErrors()) {
return "registerForm";
}
return "registerInfo";
}
}
2-2. 자동 검증
- UserValidator를 WebDataBinder의 로컬 validator로 등록한다.
- 검증할 매개변수 앞에 @Valid 어노테이션을 붙인다.
- @Valid를 사용하기 위해 Maven Repository(https://mvnrepository.com/)에서 Bean Validation API를 검색하여 Pom.xml에 의존성을 추가하고 javax.validation.Valid를 import 한다.
* Pom.xml 수정 후에는 반드시 프로젝트 우클릭-Maven-Update Project를 진행한다.
import javax.validation.Valid;
@Controller
public class RegisterController {
@InitBinder
public void toDate(WebDataBinder binder) {
SimpleDateFormat df = new SimpleDateFormat("yyyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(df, false)); //타입으로 지정
binder.registerCustomEditor(String[].class, "hobby", new StringArrayPropertyEditor("#")); //타입과 이름으로 지정
binder.setValidator(new UserValidator()); //UserValidator를 WebDataBinder의 로컬 validator로 등록
}
@PostMapping("/register/save")
public String save(@Valid User user, BindingResult result, Model m) throws UnsupportedEncodingException {
return "registerInfo";
}
}
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
3. 전역 Validator를 사용하는 방법
- 전역으로 사용할 Validator 클래스를 생성한다.
- servlet-context.xml에 등록한다.
- @InitBinder 메서드에서 setValidator 부분을 제거하거나, 로컬 Validator를 사용할 거라면 addValidator의 형태로 수정한다.
public class GlobalValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return User.class.isAssignableFrom(clazz); // clazz가 User 또는 그 자손인지 확인
}
@Override
public void validate(Object target, Errors errors) {
System.out.println("GlobalValidator.validate() is called");
User user = (User)target;
String id = user.getId();
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "pwd", "required");
if(id==null || id.length() < 5 || id.length() > 12) {
errors.rejectValue("id", "invalidLength", new String[] {"","5","12"}, null);
}
}
}
@Controller
public class RegisterController {
@InitBinder
public void toDate(WebDataBinder binder) {
SimpleDateFormat df = new SimpleDateFormat("yyyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(df, false)); //타입으로 지정
binder.registerCustomEditor(String[].class, "hobby", new StringArrayPropertyEditor("#")); //타입과 이름으로 지정
//binder.addValidators(new UserValidator()); //전역 Validator(GrobalValidator)를 만들고 로컬 validator를 추가
}
//@RequestMapping(value="/register/save", method=RequestMethod.POST)
@RequestMapping("/register/save")
public String save(@Valid User user, BindingResult result, Model m) throws UnsupportedEncodingException {
if(result.hasErrors()) {
return "registerForm";
}
return "registerInfo";
}
}
<!-- servlet-context.xml 추가 -->
<annotation-driven validator="globalValidator"/>
<beans:bean id = "globalValidator" class="경로.ch2.GlobalValidator"/>
4. MessageSource : 유효성 검증 에러 메시지 출력
- MessageSource는 Validator뿐만 아니라 다양한 메시지를 읽기 위한 인터페이스이다.
- 메시지 프로퍼티 파일을 생성(UTF-8)하고 파일을 읽기 위해 servlet-context.xml에 등록한다.
- 생성한 Validator 클래스에서 유효성 검사 후 에러 처리 부분에서 지정한 에러코드를 key 값으로 메시지 프로퍼티에서 메시지를 읽어온다.
- 검증 메시지의 출력을 위해서는 스프링이 제공하는 커스텀 태그 라이브러리를 사용해 form을 생성하고 출력할 수 있다.
//error_message.properties
required=필수항목입니다
required.user.pwd=패스워드는 필수항목입니다
invalidLength.id=아이디의 길이는 {1}~{2}사이여야합니다.
public class GlobalValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return User.class.isAssignableFrom(clazz); // clazz가 User 또는 그 자손인지 확인
}
@Override
public void validate(Object target, Errors errors) {
System.out.println("GlobalValidator.validate() is called");
User user = (User)target;
String id = user.getId();
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "pwd", "required");
if(id==null || id.length() < 5 || id.length() > 12) {
errors.rejectValue("id", "invalidLength", new String[] {"","5","12"}, null);
//invalidLength 에러코드에서 메시지 내부의 각 값에 들어갈 내용 설정
}
}
}
<form:form modelAttribute="user" action="/ch2/register/save">
<div class="title">Register</div>
<div id="msg" class="msg"><form:errors path="id"/></div> <!-- id에 관한 에러메시지 출력 부분 -->
<label for="">아이디</label>
<input class="input-field" type="text" name="id" placeholder="8~12자리의 영대소문자와 숫자 조합">
<label for="">비밀번호</label>
<input class="input-field" type="text" name="pwd" placeholder="8~12자리의 영대소문자와 숫자 조합">
</form:form>
'WEB > spring' 카테고리의 다른 글
[Spring] @Transactional (0) | 2022.06.22 |
---|---|
[Spring] Controller, Service, DAO, DTO (0) | 2022.06.21 |
[Spring] AOP 개념 및 라이브러리 설치 (0) | 2022.06.17 |
[Spring] DI, 어노테이션 정리 (0) | 2022.06.16 |
[Spring] @Annotation 어노테이션 정리 (0) | 2022.05.25 |
댓글