Data Binding 추상화 - PropertyEditor
Data Binding 이란 ?
- 기술적인 관점 : 프로퍼티 값을 타겟 객체에 설정하는 기능
- 사용자 관점 : 사용자 입력값을 애플리케이션 도메인 모델에 동적으로 변환해 넣어주는 기능
- 입력값은 대부분 문자열인데, 그 값을 객체가 가지고 있는 int, long, Boolean, Date 등 심지어 Event, Book 같은 도메인 타입으로도 변환해서 넣어주는 기능.
org.springframework.validation.DataBinder
- Spring이 제공해주는 Data Bind interface
PropertyEditor
- 주로 Web MVC에서 주로 사용하지만 그 뿐만 아니라 ApplicationContext의 XML 설정 파일에 입력한 정보(문자열)를 Bean이 가지고 있는 적절한 타입으로 변환할 때에도 쓰이며 Spring Expression Language에서도 사용된다.
- Web MVC에서만 특화된 것이 아니고 여러곳에서 쓰이는 Spring 의 핵심 기술
- 스프링 3.0 이전까지 DataBinder가 변환 작업 사용하던 인터페이스
- Thread Safe하지 않다. (상태 정보 저장 하고 있음, 따라서 Bean으로 등록하여 사용하면 위험하다!)
- Object와 String 간의 변환만 할 수 있어, 사용 범위가 제한적이다.
PropertyEditor 사용해 보기
1. Event, Controller 생성
Event.java
public class Event {
Integer id;
String title;
}
Construct, Getter, Setter, toString 생략
EventController.java
@RestController
public class EventController {
@GetMapping("/event/{event}")
public String getEvent(@PathVariable Event event) {
System.out.println(event);
return event.getId().toString();
}
}
2. MockMVC를 활용한 테스트 코드 작성
test/EventControllerTest.java
@WebMvcTest
public class EventControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void getTest() throws Exception {
mockMvc.perform(get("/event/1"))
.andExpect(status().isOk())
.andExpect(content().string("1"));
}
}
결과는 아래와 같다.
...
2019-05-24 12:41:03.131 WARN 33698 --- [ main] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type 'com.springstudy.databinding.Event'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.springstudy.databinding.Event': no matching editors or conversion strategy found]
...
java.lang.AssertionError: Status
Expected :200
Actual :500
...
테스트 코드로 부터 전달받은 값은 String 이지만 Controller에서는 Event 타입으로 받기 때문!
문제 해결
PropertyEditor 구현
EventEditor.java
public class EventEditor extends PropertyEditorSupport {
@Override
public String getAsText() {
Event event = (Event) getValue();
return event.getId().toString();
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(new Event(Integer.parseInt(text)));
}
}
getValue(), setValue() 여기서 Value란 PropertyEditor가 가지고 있는 값
서로 다른 Thread에게 공유가 된다 - Thread Safe하지 않다!!
따라서 Bean으로 등록하지 않는 것이 안전하며 꼭 등록해야 할 경우 Thread Scope의 Bean으로 등록해서 사용해야 한다.
Bean으로 등록하지 않고 사용하는 방법
@InitBinder 사용
EventController.java
public class EventEditor extends PropertyEditorSupport {
@Override
public String getAsText() {
Event event = (Event) getValue();
return event.getId().toString();
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(new Event(Integer.parseInt(text)));
}
}
위와 같이 등록하여야 한다.
Project Repository
- https://github.com/Seongmun-Hong/SpringStudy
Reference
- https://www.inflearn.com/course/spring-framework_core