1. WebDataBinder
URL 요청을 위와 같이 했을 때 Query String에 있는 year, month, day는 parameter Map 형태로 데이터가 담긴다. Controller에서 Mydate 매개 변수 타입으로 받으면 오른쪽 객체가 만들어진다. 요청할 때 넘어온 year, month, day를 binding할 때 중간에 WebDataBinde가 타입 변환, 데이터 검증 두 가지 역할을 한다.
parameter Map 자료형은 String이고 Mydate 자료형은 int이므로 타입이 불일치 해서 형변환을 해야한다. 타입을 변환하고 결과는 BindingResult에 담고 에러가 발생하면 같이 저장한다. month는 1~12값을 가져야해서 타입을 변환한 다음에 데이터 검증을 한다. 문제가 없다면 Binding해서 Mydate 객체가 만들어지고, 문제가 있다면 에러를 BindngResult에 담는다. BindngResult는 Controller에게 전달되어 결과를 처리한다.
2. RegisterController에 변환기능 추가하기 - 실습
RegisterController은 회원가입으로 registerForm에서 넘어온 데이터를 받아서 User 객체에 담는다. 따라서 save( User user)로 User 매개 변수가 있어야한다. Binding 하기 위해서는 save( User user, Bindingresult result )로 Bindingresult 타입의 매개 변수를 선언해야 Bindingresult에 저장한 데이터와 에러를 사용할 수 있다.
sns는 'sns=카카오톡&sns=인스타그램'으로 요청을 받아 String이면 "카카오, 인스타그램" String 배열이면 ["카카오", 인스타그램"]으로 spring이 알아서 변환을 한다. 하지만 생일은 String "2021/12/31"으로 요청받으면 birth의 Date 타입으로 형변환은 spring이 알아서 변환하지 않는다. 그냥 타입을 변환할 때 에러가 발생해 에러가 어떤식으로 나타나는지 보고 Date 타입으로 형변환을 위해 변환기 registerCustomEditor를 사용해서 변환 메서드를 만든다.
날짜의 문자열이 년, 월, 일로 되어 있다는 것을 "yyyy/mm/dd"로 알려줘야 한다. 나라마다 "2021/12/31"을 "12/31/2021" 등 날짜를 다양한 문자열로 쓰기 때문에 날짜 문자열이 어떤 형식인지 알려줘야 한다. 메서드를 추가 하지 않고 @DateTimeFormat 어노테이션을 사용해 필드에 직접 적을 수 있다.
RegisterController의 유효성 검사를 true로 바꿔 입력을 무조건 받게한다.
Query String은 String 배열이고 sns는 String으로 binding된다. String 배열은 원래 String으로 binding되지 않는다. spring이 이 작업을 자동으로 해준다. 왼쪽 결과를 보면 콤마를 구분자로 String 변환했다. 날짜는 타입이 String으로 되어 있는데 이를 Date로 바꾸면 어떻게 될까?
User의 String birth를 Date birth로 일괄 고친다. import는 "java.util.Date"로 한다.
String에서 Date로 바뀌었으므로 getBirth 반환 타입을 바꾼다.
회원가입을 하면 잘? 출력된다. 그 이유는 spring이 '/'를 구분자로 인식하기 때문이다. 회원가입 할 때 날짜 구분자를 '-'로 바꾼다.
404 에러가 발생한다. "2020-12-31" 날짜를 Date 타입으로 변환하지 못했기 때문이다.
BindingResult를 추가하는데 위치는 User 바로 다음에 와야한다.
요청을 하면 아까와는 다르게 에러는 발생하지 않는다. 실제로 birth에 null이 들어간다.
에러가 안뜨는거 뿐이지 발생하긴한다. 이 결과가 BindingResul에 전달되는 것이다. Field error in object 'user' on field 'birth': rejected value [2020-12-31]에서 "2020-12-31"이 필드에서 거절당했다는 내용이 출력된다. 거절 이유는 typeMismatch이다.
InitBinder를 추가한다. Date.class로 변환할 때 CustomDateEditor 클래스를 이용한다. df는 형식만 지정해준 것이고 false는 빈 값을 허용하는지 여부이다. 타입을 변환할 때 먼저 CustomDateEditor를 확인한다. 만약 "yyyy-MM-dd" 형식이 있으면 사용하고 없으면 spring에 있는 default 기본 내장 기능을 사용한다. 메서드 이름은 바꿔도 되지만 매개 변수 형식은 WebDataBinder로 써야한다.
회원 가입을 하면 잘 출력된다.
에러도 발생하지 않는다.
sns를 배열로 바꾸고 출력 부분을 배열 출력으로 바꾼다.
sns에 배열로 잘 넘어오긴했다. jsp 바꾸자.
registerForm에 취미 항목을 만든다.
User에 hobby를 배열로 만들고 Getter, Setter를 만든다.
취미를 입력하면 브라우저에는 나오지 않지만 console에 나온다.
console에 취미가 나눠서 들어가지 않고 하나의 문자열로 들어갔다.
StringArrayPropertyEditor를 이용하면 '#'를 구분자로 인식해서 String 배열로 바꾸라는 말이다.
jsp에 hobby를 추가한다.
hobby가 잘 출력된다.
아까와는 다르게 취미가 쉼표로 구분되어 출력된다.
RegisterController에 있는 것을 주석처리 하고 User에 직접 @DateTimeFormat 어노테이션을 사용한다.
날짜 변환이 잘 된다. @InitBinder를 이용해서 날짜 변환을 할 수 있지만 컨트롤러 내에서만 사용할 수 있고 모든 컨트롤러에 사용하기 위해서는 WebBindingInitializer를 사용해야한다.
3. PropertyEditor
PropertyEditor는 양방향 타입 변환을 해준다.
컨트롤러에서 위와같이 쓰면 특정 필드에만 변환할 수 있게 만들 수 있다.
디폴트 PropertyEditor는 스프링이 기본적으로 제공하고 커스텀 PropertyEditor를 사용자가 직접 만들 수 있다. PropertyEditorSupport를 상속받아 overriding하면 편하다. @InitBinder를 이용해서 특정 컨트롤 내에서의 변환만 할 수 있고, WebBindingInitializer를 사용해서 모든 컨트롤러에서 변환하게 할 수 있다.
구글에 검색하면 PropertyEditor 목록 확인할 수 있다.
4. Converter와 ConversionService
Converter는 단방향 변환으로 양방향으로 사용하려면 Converter 2개 사용하면 된다. PropertyEditor는 양방향 이지만 단점이 iv 인스턴스 변수 이다. PropertyEditor는 자바 객체인 JavaBeans에서 왔는데 인스턴스 변수이기 때문에 stateful이다. 즉 싱글톤으로 여러번 쓸 수 없고 사용할 때마다 새로운 객체를 생성해야한다. Converter는 stateless이다.
Converter는 인터페이스가 있어 Converter<String, String[ ] >는 String에서 String[ ]로 변환한다.
여러 Converter는 ConversionService에 등록하면 자동으로 타입 변환을 할 수 있다. 기본적으로 WebDataBinder에 DefaultFormattingConversionService가 있다. 많은 Converter가 이 안에 들어있다.
5. Formatter
Formatter는 양방향이므로 PropertyEditor와 비슷하다. Formatter는 interface이고 부모 Printer, Parser를 상속받는다. Print는 Object -> String으로, Parse는 String->Object로 변환해서 양방향이다. @NumberFormat은 Object가 숫자 타입이고 @DateTimeFormat은 Object가 날짜 타입이다.
ConversionService을 추가하면
엄청 많은 Converter가 등록되어 있는 것을 볼 수 있다.
타입 변환에는
1. 커스텀 PropertyEditor
2. ConversionService
3. Default PropertyEditor
가 있고 숫자는 우선 순위이다.
'스프링의 정석 > Ch. 02 Spring MVC' 카테고리의 다른 글
33. 프로젝트 export와 import (0) | 2023.08.23 |
---|---|
32. IntelliJ 설치 - Windows (0) | 2023.08.23 |
29. DispatcherServlet 파헤치기 (0) | 2023.08.18 |
27. 예외처리(1) - 실습 (0) | 2023.08.16 |
26. 세션(Session) - 실습(2) (0) | 2023.08.15 |