1. WebDataBinder
- RegisterController에 변환기능 추가하기
import java.util.Date;
public class User {
private String id;
private String pwd;
private String name;
private String email;
private Date birth;
private String sns;
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", pwd='" + pwd + '\'' +
", name='" + name + '\'' +
", email='" + email + '\'' +
", birth='" + birth + '\'' +
", sns='" + sns + '\'' +
'}';
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public String getSns() {
return sns;
}
public void setSns(String sns) {
this.sns = sns;
}
}
- String birth를 Date birth로 변환
- Spring이 바꿔줄수 있는 타입이라 변환되었다.
- 패턴을 2020-12-31로 바꾸면?
- 에러가 나온다.
public String save(User user, Model m) throws Exception{
// 1. 유효성 검사
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)
}
// 2. DB에 신규회원 정보를 저장
return "registerInfo";
}
- BindingResult 추가 (주의) 바인딩할 객체 바로 뒤에 와야한다.
public String save(User user, BindingResult result,Model m) throws Exception{
System.out.println("result="+result);
// 1. 유효성 검사
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)
}
// 2. DB에 신규회원 정보를 저장
return "registerInfo";
}
- 다시 실행하면?
- 에러가 나오지 않는 모습
- birth에 null 이 들어갔고... 변환에 실패했다.
- BindingResult : 예외가 발생하면 컨트롤러에게 바인딩 결과를 주고 컨트롤러가 처리하게 한다.
- 에러가 발생하지 않은게 아니라 에러페이지로 이동하지 않은 것 뿐이다.
- typeMismatch : 타입이 맞지 않아서 생긴에러
@InitBinder
public void toDate(WebDataBinder binder){
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(df,false));
}
- toDate메서드를 RegisterController클래스에 정의 해준다.
- new CustomDateEditor('형식지정', '빈값 허용/비허용')
- 변환 성공
- 에러도 발생하지 않았다.
- sns타입을 String배열로 바꿔보자...
public class User {
private String id;
private String pwd;
private String name;
private String email;
private Date birth;
private String[] sns;
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", pwd='" + pwd + '\'' +
", name='" + name + '\'' +
", email='" + email + '\'' +
", birth='" + birth + '\'' +
", sns='" + Arrays.toString(sns) + '\'' +
'}';
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public String[] getSns() {
return sns;
}
public void setSns(String[] sns) {
this.sns = sns;
}
}
- 콘솔에서는 잘나온다.
- loginForm.jsp의 input에 취미를 추가
- User클래스에 getter, setter, toString, String배열을 hobby를 선언
public class User {
private String id;
private String pwd;
private String name;
private String email;
private Date birth;
private String[] hobby;
private String[] sns;
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", pwd='" + pwd + '\'' +
", name='" + name + '\'' +
", email='" + email + '\'' +
", birth='" + birth + '\'' +
", hobby=" + Arrays.toString(hobby) +
", sns='" + Arrays.toString(sns) + '\'' +
'}';
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public String[] getSns() {
return sns;
}
public void setSns(String[] sns) {
this.sns = sns;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
}
- 재시작하면...
- 화면에는 나오지 않았지만...
- 배열에 취미들이 하나의 문자로 들어가버렸다.
- 나눠주려면?
@InitBinder
public void toDate(WebDataBinder binder){
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(df,false));
}
@InitBinder
public void toCustom(WebDataBinder binder){
binder.registerCustomEditor(String[].class, new StringArrayPropertyEditor("#"));
}
- registerInfo.jsp에 hobby추가
<body>
<h1>id=${user.id}</h1>
<h1>pwd=${user.pwd}</h1>
<h1>name=${user.name}</h1>
<h1>email=${user.email}</h1>
<h1>birth=${user.birth}</h1>
<h1>hobby=${user.hobby}</h1>
<h1>sns=${user.sns}</h1>
</body>
- 다시 회원가입을 눌러주면...
- 배열로 잘 들어간 모습
- @DateTimeFormat(pattern=" ")
public class User {
private String id;
private String pwd;
private String name;
private String email;
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birth;
private String[] hobby;
private String[] sns;
}
// @InitBinder
// public void toDate(WebDataBinder binder){
// SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
// binder.registerCustomEditor(Date.class, new CustomDateEditor(df,false));
// }
@InitBinder
public void toCustom(WebDataBinder binder){
binder.registerCustomEditor(String[].class, new StringArrayPropertyEditor("#"));
}
- 주석처리후 다시 실행해보면..
- 잘 변환된 모습
- @InitBinder는 컨트롤러 내에서만 적용가능하다.
2. PropertyEditor : 양방향 타입 변환(String -> 타입, 타입 -> String), 특정 타입이나 이름의 필드에 적용 가능
@InitBinder
public void toCustom(WebDataBinder binder){
binder.registerCustomEditor(String[].class, "hobby", new StringArrayPropertyEditor("#"));
}
- 이름 지정을 해주면 지정한 필드에만 패턴이 적용된다.
- 디폴트 PropertyEditor - 스프링이 기본적으로 제공
- 커스텀 PropertyEditor - 사용자가 직접 구현. PropertyEditorSupport를 상속하면 편리
- 모든 컨트롤러 내에서의 변환 - WebBindingInitializer를 구현후 등록
- 특정 컨트롤러 내에서의 변환 - 컨트롤러에 @InitBinder가 붙은 메서드를 작성
3. Converter : 단방향 타입 변환(타입A -> 타입B), PropertyEditor의 단점을 개선(stateful -> stateless)
- PropertyEditor의 단점 : 인스턴스 변수를 사용 -> 싱글톤 사용 불가능
public class StringToStringArrayConverter implements Converter<String, String[]> {
@Override
public String[] convert(String source) {
return source.split("#"); // String -> String[]
}
}
- Converter<타입A, 타입B> -> 타입A에서 타입B로 변환된다.
- ConversionService - 타입 변환 서비스를 제공, 여러 Converter를 등록 가능
- WebDataBinder에 DefaultFormattingConversionService이 기본등록
- 모든 컨트롤러 내에서의 변환 - ConfigurableWebBindingInitializer를 설정해서 사용
- 특정 컨트롤러 내에서의 변환 - 컨트롤러에 @InitBinder가 붙은 메서드를 작성
4. Formatter
- 양방향 타입 변환(String -> 타입, 타입 -> String)
- 바인딩할 필드에 적용 - @NumberFormat, @DateTimeFormat
public interface Formatter<T> extends Printer<T>, Parser<T> {
}
public interface Printer<T> {
String print(T object, Locale locale); // Object -> String
}
public interface Parser<T> {
T parse(String text, Locale locale) throws ParseException; // String -> Object
}
@DateTimeFormat(pattern="yyyy/MM/dd")
Date birth;
@NumberFormat(pattern="###,###")
BigDecimal salary;
- ConversionService에 들어있는 데이터를 보려면...
@InitBinder
public void toDate(WebDataBinder binder){
ConversionService conversionService = binder.getConversionService();
System.out.println("conversionService="+conversionService);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(df,false));
}
@InitBinder
public void toCustom(WebDataBinder binder){
binder.registerCustomEditor(String[].class, "hobby", new StringArrayPropertyEditor("#"));
}
- ConversionService에 binder.getConversionService()를 넣어주고... 재시작을 해주면
- ConversionService에 등록된 converter들을 볼 수 있다.
- WebDataBinder의 우선순위
- 커스텀 PropertyEditor
- ConversionService
- 디폴트 PropertyEditor
'Spring' 카테고리의 다른 글
38. 데이터의 변환과 검증(2) - 패스트캠퍼스 백엔드 부트캠프 3기 (2) | 2025.02.17 |
---|---|
36. DispatcherServlet - 패스트캠퍼스 백엔드 부트캠프 3기 (0) | 2025.02.17 |
35. 예외처리/이론 - 패스트캠퍼스 백엔드 부트캠프 3기 (2) | 2025.02.17 |
34. 예외처리/실습 - 패스트캠퍼스 백엔드 부트캠프 3기 (0) | 2025.02.17 |
33. 세션(session)/실습(3) - 패스트캠퍼스 백엔드 부트캠프 3기 (2) | 2025.02.16 |