스프링 3.0 이전까지 사용되던 PropertyEditor는 Object-String 간의 변환만 지원하며 구현도 번거롭고 스레드 세이프 하지 않기 때문에 빈으로 등록해서 사용할 수도 없다.
그래서 스프링 3.0 이후부터 PropertyEditor의 단점을 없앤 Converter라는 인터페이스가 등장했다.
public class StringToEventConverter implements Converter<String, Event> {
@Override
public Event convert(String source) {
Event event = new Event();
event.setId(Integer.parseInt(source));
return event;
}
}
도메인 클래스
public class Event {
private Integer id;
private String title;
public Event(Integer id){
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "Event{" +
"id=" + id +
", title='" + title + '\'' +
'}';
}
}
public class EventConverter {
@Component
public static class StringToEventConverter implements Converter<String, Event> {
@Override
public Event convert(String source) {
return new Event(Integer.parseInt(source));
}
}
@Component
public static class EventToStringConverter implements Converter<Event, String>{
@Override
public String convert(Event source) {
return source.getId().toString();
}
}
}
ConverterRegistry에 등록
@Component
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new EventConverter.StringToEventConverter());
//registry.addFormatter(new EventFormatter());
}
}
그러나 ConverterRegistry에 등록하지 않더라도 기본적으로 integer나 String과 도메인 클래스의 변환은 기본적으로 등록되어있는 Converter나 Formatter가 자동으로 변환해준다.
Converter의 변환은 General하나, web에서는 사용자의 입력값이 주로 문자열로 들어오고, 또 객체들을 문자로 내보내는 경우가 많고 이 문자열을 MessageSource를 이용해 Locale에 맞게 변환해 메시지를 보내주는 경우가 많다. 따라서 web쪽에 특화되어있는 인터페이스를 스프링에서 제공하고 있는데, 이것이 바로 Formatter이다.
public class EventFormatter implements Formatter<Event> {
@Override
public Event parse(String text, Locale locale) throws ParseException {
Event event = new Event();
int id = Integer.parseInt(text);
event.setId(id);
return event;
}
@Override
public String print(Event object, Locale locale) {
return object.getId().toString();
}
}
Formatter를 구현하게 되면 parse, print라는 두 메소드를 오버라이딩 해야 한다. parse는 문자열을 받아서 객체로 변환하고, print는 객체를 받아서 문자열로 변환한다. 이는 PropertyEditor(getAsText(), setAsText(String text))와 비슷한데, Formatter는 Locale 정보를 받아 변환할 수 있다.
또한 Formatter도 스레드 세이프 하기 때문에 빈으로 등록해서 사용할 수 있다.
@Component
public class EventFormatter implements Formatter<Event> {
@Override
public Event parse(String text, Locale locale) throws ParseException {
return new Event(Integer.parseInt(text));
}
@Override
public String print(Event object, Locale locale) {
return object.getId().toString();
}
}
또한 Locale 정보를 받아 메시지 정보를 만들고 싶으면 빈으로 등록되어 있기 때문에 MessageSource를 @Autowired를 통해 주입받을 수 있다.
@Component
public class EventFormatter implements Formatter<Event> {
@Autowired
MessageSource messageSource;
@Override
public Event parse(String text, Locale locale) throws ParseException {
return new Event(Integer.parseInt(text));
}
@Override
public String print(Event object, Locale locale) {
messageSource.getMessage("title", locale);
return object.getId().toString();
}
}
FormatterRegistry에 등록
@Component
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
//registry.addConverter(new EventConverter.StringToEventConverter());
registry.addFormatter(new EventFormatter());
}
}
PropertyEditor를 DataBinder(org.springframework.validation.DataBinder)를 통해 사용했다면, Converter와 Formatter는 ConversionService를 통해 사용하게 된다.
Converter는 ConverterRegistry에 등록해야 되고, Formatter는 FormatterRegistry에 등록해야 하나, 사실 FormatterRegistry는 ConverterRegistry를 상속받고 있다. 그래서 FormatterRegistry에는 Converter도 등록할 수 있다. 또한 DefaultFormattingConversionService는 ConversionService 또한 구현하고 있다. 이를 확인하려면 다음과 같이 코드를 작성하면 된다.
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ConversionService conversionService;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(conversionService);
System.out.println(conversionService.getClass().toString());
}
}
그러나 스프링 부트에서 이를 출력해보면 DefaultFormattingConversionSerivce가 아닌 WebConversionService가 나온다.
스프링 부트
또한 Formatter와 Converter가 빈으로 등록되어 있다면 스프링 부트가 자동으로 WebConversionService에 등록해준다.
그리고, 테스트 코드를 작성할 때 @WebMvcTest라는 애노테이션을 사용하면 웹과 관련된 빈만 등록해주기 때문에 Converter와 Formatter도 테스트하겠다고 빈으로 등록해주는 것이 좋다.
RunWith(SpringRunner.class)
@WebMvcTest({EventFormatter.class, EventController.class})
@WebMvcTest({EventConverter.EventToStringConverter.class, EventConverter.StringToEventConverter.class, EventController.class})
public class EventControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void getTest() throws Exception{
mockMvc.perform(get("/event/1"))
.andExpect(status().isOk())
.andExpect(content().string("1"));
}
}
참고