문제 상황
- 2000/11/29와 같이 입력하면 Date형식에 맞게 잘 출력된다
스프링이 기본적으로 ../../.. 형식을 Date로 변환할 수 있기 때문
- 구분자를 - 로 바꾼다면 2000-11-29 에러 발생
이 형식은 스프링이 Date 타입으로 변환해주지 않기 때문
해결방법
- 컨트롤러에 BindingResult 타입 객체를 매개변수로 삽입
바인딩할 객체 바로 뒤에 자리해야한다!public String save(User user, BindingResult result, Model m)
- BindingResult를 출력해보면,
에러 페이지는 나오지 않지만 아무것도 나오지 않는다
출력결과 - (변환에서 에러가 하나 발생하여 BindingResult에 에러가 전달되었다)
result= org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'user' on field 'birth': rejected value [2000-11-29]; codes [typeMismatch.user.birth,typeMismatch.birth,typeMismatch.java.util.Date,typeMismatch];
- 클라이언트에게 다시 작성하라고 redirect할 수도 있고,
- InitBinder 메서드를 선언할 수 있다
메서드를 만들면, 타입을 변환할 때 이 메서드를 먼저 확인한다실행결과 -@InitBinder public void toDate(WebDataBinder binder){ //매개변수로 꼭 넣어줘야함! SimpleDateFormat df = new SimpleDateFormat("yyyy-mm-dd"); binder.registerCustomEditor(Date.class, new CustomDateEditor(df, false)); }
- 기존 sns를 체크하면 String으로 연결되어 전달했는데, 각각의 요소를 배열로 만들어 user에 저장하도록 한다
객체의 타입을 String[]으로 바꾸면 입력된 데이터가 각각 나눠져서 저장이 된다- String[]타입의 멤버 hobby를 추가해보자
hobby는 form에서 sns처럼 체크박스가 아닌 text 형태로 값을 입력받는다문제상황
- text 형태로 hobby를 여러개 입력받으면 자동으로 구분자로 나눠서 배열에 저장되지 않는다
- 예를 들어 tennis#piano#swimming 을 입력했을 때 [tennis, piano, swimming]이 아닌 [tennis#piano#swimming] 으로 하나로 인식된다
- 각각의 문자열이 배열로 들어가게 바꿔주어야 한다
해결방법
- InitBinder에 구분자(여기선 #)로 String을 나눠 String[]에 저장하는 메서드를 사용한다
binder.registerCustomEditor(String[].class, new StringArrayPropertyEditor("#"));
실행결과 -
[tennis, piano, swimming] 각각 나뉘어서 잘 저장되었다
User 클래스에서 멤버로 birth를 선언할 때 애노테이션으로 @DateTimeFormat(pattern="yyyy-mm-dd")를 붙이면 컨트롤러에 데이터 변환을 설정하지 않아도 데이터가 변환된다
binder.registerCustomEditor(String[].class, "hobby", new StringArrayPropertyEditor("#"));
StringArray라는 PropertyEditor를 hobby라는 특정 필드에만 적용하겠다는 의미,T
>, Parser<T
> 인터페이스를 상속하고 있다<?>
clazz) 메서드 : 이 검증기로 검증 가능한 객체인지 알려주는 메서드servlet-context.xml에
<annotation-driven validator="globalValidator"/>
<beans:bean id="globalValidator" class="com.fastcampus.ch2.GlobalValidator"/>
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; //target이 Object형이기 때문에 형변환
String id = user.getId();
// if(id==null || "".equals(id.trim())) {
// errors.rejectValue("id", "required");
// }
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "pwd", "required");
if(id==null || id.length() < 5 || id.length() > 12) {
errors.rejectValue("id", "invalidLength");
}
}
}
//유효성 검사
if(!isValid(user)) {
String msg = URLEncoder.encode("id를 잘못입력하셨습니다.", "utf-8");
m.addAttribute("msg", msg);
return "forward:/register/add";
return "redirect:/register/add?msg="+msg; // URL재작성(rewriting)
}
------------------------------------------------------------------------
//수동 검증 - validator를 직접 생성하고 validate()를 직접 호출했음
UserValidator userValidator = new UserValidator();
userValidator.validate(user, result);
//user 객체를 검증한 결과 에러가 있으면, registerForm을 이용해서 에러를 보여준다
if(result.hasErrors()){
return "registerForm";
}
//자동 검증
binder.setValidator(new UserValidator()); //UserValidator를 WebDataBinder의 로컬 validator로 등록
public String save(@Valid User user, BindingResult result, Model m) throws Exception
2.0.1 final
버전을 선택하고 Maven 코드를 클릭하여 복사한다 <annotation-driven validator="globalValidator"/>
<beans:bean id="globalValidator" class="com.fastcampus.ch2.GlobalValidator"/>
UserValidator를 직접 등록했고 (add)
GlobalValidator를 xml을 이용해 등록했기 때문에 리스트에 두 validator가 등록되어 있는 것을 알 수 있다
Global Validator만 사용하고 싶다면 add한 부분을 삭제하면 된다
ResourceBundleMessageSource
를 등록 <beans:bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basenames">
<beans:list>
<beans:value>error_message</beans:value> //아래와 이름이 error_message로 같아야 한다
<!-- /src/main/resources/error_message.properties -->
</beans:list>
</beans:property>
<beans:property name="defaultEncoding" value="UTF-8"/>
</beans:bean>
getMessage() 메서드
@Override
public String getMessage(String code, Object[] args, String defaultMessage, Locale locale) {
return null;
}
@Override
public String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException {
return null;
}
@Override
public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
return null;
}
어떤 code
를 주면 그에 대한 메세지를 반환하는 것
에러 코드에 대한 메세지가 있는 파일을 생성해야 한다
이름과 확장자는 error_message.properties
메세지 코드 = 메세지로 구성되어 있음
국가별로 다른 메세지 파일을 지정할 수 있다
error_message.properties 는 디폴트
error_message_ko.properties 는 getMessage의 locale이 ko일 때
error_message_en.properties 는 getMessage의 locale이 en일 때
getMessage의 args 값으로 메세지에 들어갈 값을 줄 수 있다
값으로 new String[]{"5", "11"}을 주면 의 {1}에 5가, {2}에 11이 들어가서 출력됨
Error에 "id" 필드에 "required" 값이 저장되었다면 에서 required.user.id
찾고 없으면 required.id
찾고 없으면 required.java.lang.String
찾고 없으면 required
찾고 그래도 없으면 default message
출력한다
위 파일에서는 id를 찾으면 required가 출력될 것이다
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<form>
대신 <form:form>
사용한다<form:form modelAttribute="user">
<form id="user" action="/ch2/register/save" method="post"> 로 변환된다
<form:errors>
로 에러를 출력한다 path에 에러 발생 필드를 지정<form:errors path="id" cssClass="msg"/> id라는 필드에서 발생한 에러 보여줌
<span id="id.errors" class="msg">필수 입력 항목입니다.</span> 로 변환된다
if(id==null || id.length() < 5 || id.length() > 12) {
errors.rejectValue("id", "invalidLength", new String[] {"5", "12"}, null);
}