springframework

o.s.w IoC 핵심기술. 데이터 바인딩 추상화: Converter와 Formatter

Jungsoomin :) 2020. 9. 16. 01:20

PropertyEditor의 단점,

  1. Object와 String과의 변환만 가능하다
  2.  Thread-Safe 하지 않은 점

이를 보완하기 위해 Converter와 Formatter가 등장했다.


Converter는 Converter 인터페이스를 구현해서 만든다.

  1. Converter<소스, 타겟> 의 제네릭을 가지며 제네릭 순서에따라 변환이 달라진다.
  2. 소스-> 타겟 변환순서는 동일하며, 함수형 인터페이스 이다.
  3. source를 받아 수동으로 원하는 타입으로 컨버팅하여 리턴한다.
import me.soomin.demospring51.PropertyEditor.Event;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

public class EvnentConverter { // 등록은 ? ConverterRegistry에 한다.

    @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();
        }
    }

}

값을 내포하지않아 Tread-Safe 하며 얼마든지 Spring Bean으로 사용할 수 있다.


등록작업 : WebMvcConfigurer 구현객체

등록작업은 ConverterRegistry 에서 하지만, 직접 사용할 일은 거의 없다고한다.

 

  1.  addFormatters 메서드를 재정의
  2. 매개변수인 FormatterRegistry 의 addConverter 메서드에 Converter 구현객체를 등록해주기만 하면된다.
import me.soomin.demospring51.ConverterAndFormatter.EvnentConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class WebMvcConfig  implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new EvnentConverter.EventToStringConverter());
    }
}

이는 곧 전역범위를 말하며 모든 컨트롤러의 실행 전에 다 동작하게 된다.

기본 랩퍼클래스등의 클래스는 이미 등록되어있는 컨버터가 변환해주므로 무조건적으로 컨버터를 만들어 사용할 이유는 없다.


Web에서 이루어지는 String 사이의 변환에 특화된 Formatter

Formatter의 사용법은 Formatter 인터페이스를 구현하는 것

  1. Formatter<변환타입> 의 제네릭을 가진다.
  2. 재정의할 메서드는 2개로
  3. parse(String text, Locale locale) : 문자를 받아 타입으로 컨버팅
  4. print(타입 obejct, Locale locale) : 타입을 받아 문자로 컨버팅
import me.soomin.demospring51.PropertyEditor.Event;
import org.springframework.format.Formatter;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.util.Locale;

@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();
    }
}

Thread-Safe 하며, 빈으로 등록이 가능하다. 이는 즉 빈을 주입받을 수 있다는 이야기이고 , 이로인해 자유롭게 빈을 받아 사용할 수 있는 것이다. MessageSource를 받아 원하는 로케일의 메세지로 컨버팅 시킬 수도 있다.

package me.soomin.demospring51.ConverterAndFormatter;

import me.soomin.demospring51.PropertyEditor.Event;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.format.Formatter;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.util.Locale;

@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();
    }
}

등록방법 은 WebMvcConfigurer로 동일. 전역범위<<

import me.soomin.demospring51.ConverterAndFormatter.EventFormatter;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class WebMvcConfig  implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new EventFormatter());
    }
}

컨버터와 포멧터는 DataBinder 대신에 ConversionService가 변환을 담당한다.

굉장히 중요한 녀석인데,  xml설정파일, SpEL @Value 에서 변환을 담당해주는 녀석이 이녀석이다.

 

가장많이 사용되는 구현객체는 DefaultFormattingConversionService 이다. 독학에서도 보던 녀석인데 WebDataBinder가 변환을 위임하는 객체로 기억하고 있다. 들어오는 값을 자바타입으로, 자바타입을 문자열로, 변환시켜주는 것으로 기억한다.

 

ConverterRegistry 를 FormatterRegistry 가 상속하고 있기에 FormatterRegistry에 컨버터를 등록해도 무방하다.

 

DefaultFormattingConversionServiceConversionServiceConverterRegistry , FormatterRegistry 의 기능도 전부 포함하고 있다는 것이다!

@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());
    }
}

콘솔<<

class org.springframework.boot.autoconfigure.web.format.WebConversionService

스프링부트:

  1. DefaultFormattingConversionService 를 상속한 자동으로 WebConversionService를 빈으로 등록한다

더욱 더 많은 기능을 가지고 있다.

또한 클래스패스에 위치한 의존에따른 Converter도 등록해주며,

 

Formatter와 Coverter 빈 객체들도  자동으로 전역 범위로 등록해준다.

즉 Formatter와 Converter를 등록하기위해 WebMvcConfigurer를 사용할 필요는 부트에는 없다. 

날짜 값을 변환해주는 DateTimeFormat 등 굉장히 많은 컨버터와 포멧터를 등록해주는데  이는 콘솔에 주입받은 CoversionService 를 찍어보면 알 수 있다.

@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());
    }
}
ConversionService converters =
	@org.springframework.format.annotation.DateTimeFormat java.lang.Long -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@577f9109,@org.springframework.format.annotation.NumberFormat java.lang.Long -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	@org.springframework.format.annotation.DateTimeFormat java.time.LocalDate -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@4b6ac111,java.time.LocalDate -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@6fe46b62
	@org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@4b6ac111,java.time.LocalDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@3068b369
	@org.springframework.format.annotation.DateTimeFormat java.time.LocalTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@4b6ac111,java.time.LocalTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@61e45f87
	@org.springframework.format.annotation.DateTimeFormat java.time.OffsetDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@4b6ac111,java.time.OffsetDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@6ecd665
	@org.springframework.format.annotation.DateTimeFormat java.time.OffsetTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@4b6ac111,java.time.OffsetTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@1ec7d8b3
	@org.springframework.format.annotation.DateTimeFormat java.time.ZonedDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@4b6ac111,java.time.ZonedDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@5491f68b
	@org.springframework.format.annotation.DateTimeFormat java.util.Calendar -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@577f9109
	@org.springframework.format.annotation.DateTimeFormat java.util.Date -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@577f9109
	@org.springframework.format.annotation.NumberFormat java.lang.Byte -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	@org.springframework.format.annotation.NumberFormat java.lang.Double -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	@org.springframework.format.annotation.NumberFormat java.lang.Float -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	@org.springframework.format.annotation.NumberFormat java.lang.Integer -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	@org.springframework.format.annotation.NumberFormat java.lang.Short -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	@org.springframework.format.annotation.NumberFormat java.math.BigDecimal -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	@org.springframework.format.annotation.NumberFormat java.math.BigInteger -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	java.lang.Boolean -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@9f6e406
	java.lang.Character -> java.lang.Number : org.springframework.core.convert.support.CharacterToNumberFactory@7e97551f
	java.lang.Character -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@23cd4ff2
	java.lang.Enum -> java.lang.Integer : org.springframework.core.convert.support.EnumToIntegerConverter@2caf6912
	java.lang.Enum -> java.lang.String : org.springframework.core.convert.support.EnumToStringConverter@3e6f3bae
	java.lang.Integer -> java.lang.Enum : org.springframework.core.convert.support.IntegerToEnumConverterFactory@12477988
	java.lang.Long -> java.time.Instant : org.springframework.format.datetime.standard.DateTimeConverters$LongToInstantConverter@534ca02b
	java.lang.Long -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$LongToCalendarConverter@3aefae67,java.lang.Long -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$LongToCalendarConverter@2e1792e7
	java.lang.Long -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$LongToDateConverter@c68a5f8,java.lang.Long -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$LongToDateConverter@69c6161d
	java.lang.Number -> java.lang.Character : org.springframework.core.convert.support.NumberToCharacterConverter@70807224
	java.lang.Number -> java.lang.Number : org.springframework.core.convert.support.NumberToNumberConverterFactory@143d9a93
	java.lang.Number -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@4159e81b
	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.lang.Long: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@577f9109,java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Long: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalDate: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@4b6ac111,java.lang.String -> java.time.LocalDate: org.springframework.format.datetime.standard.TemporalAccessorParser@591fd34d
	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@4b6ac111,java.lang.String -> java.time.LocalDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@17ca8b92
	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@4b6ac111,java.lang.String -> java.time.LocalTime: org.springframework.format.datetime.standard.TemporalAccessorParser@7c9b78e3
	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.OffsetDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@4b6ac111,java.lang.String -> java.time.OffsetDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@45394b31
	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.OffsetTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@4b6ac111,java.lang.String -> java.time.OffsetTime: org.springframework.format.datetime.standard.TemporalAccessorParser@3b0ca5e1
	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.ZonedDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@4b6ac111,java.lang.String -> java.time.ZonedDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@736ac09a
	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Calendar: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@577f9109
	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Date: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@577f9109
	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Byte: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Double: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Float: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Integer: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Short: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigDecimal: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigInteger: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3bc735b3
	java.lang.String -> java.lang.Boolean : org.springframework.core.convert.support.StringToBooleanConverter@400d912a
	java.lang.String -> java.lang.Character : org.springframework.core.convert.support.StringToCharacterConverter@5b5caf08
	java.lang.String -> java.lang.Enum : org.springframework.core.convert.support.StringToEnumConverterFactory@7a94b64e
	java.lang.String -> java.lang.Number : org.springframework.core.convert.support.StringToNumberConverterFactory@40226788
	java.lang.String -> java.nio.charset.Charset : org.springframework.core.convert.support.StringToCharsetConverter@1d01dfa5
	java.lang.String -> java.time.Duration: org.springframework.format.datetime.standard.DurationFormatter@74fef3f7
	java.lang.String -> java.time.Instant: org.springframework.format.datetime.standard.InstantFormatter@5bb3131b
	java.lang.String -> java.time.Month: org.springframework.format.datetime.standard.MonthFormatter@69eb86b4
	java.lang.String -> java.time.MonthDay: org.springframework.format.datetime.standard.MonthDayFormatter@5bb8f9e2
	java.lang.String -> java.time.Period: org.springframework.format.datetime.standard.PeriodFormatter@54dcbb9f
	java.lang.String -> java.time.Year: org.springframework.format.datetime.standard.YearFormatter@2a037324
	java.lang.String -> java.time.YearMonth: org.springframework.format.datetime.standard.YearMonthFormatter@585ac855
	java.lang.String -> java.util.Currency : org.springframework.core.convert.support.StringToCurrencyConverter@d400943
	java.lang.String -> java.util.Locale : org.springframework.core.convert.support.StringToLocaleConverter@73d69c0f
	java.lang.String -> java.util.Properties : org.springframework.core.convert.support.StringToPropertiesConverter@31ff1390
	java.lang.String -> java.util.TimeZone : org.springframework.core.convert.support.StringToTimeZoneConverter@6d64b553
	java.lang.String -> java.util.UUID : org.springframework.core.convert.support.StringToUUIDConverter@781a9412
	java.lang.String -> me.soomin.demospring51.PropertyEditor.Event: me.soomin.demospring51.ConverterAndFormatter.EventFormatter@6a933be2,java.lang.String -> me.soomin.demospring51.PropertyEditor.Event: me.soomin.demospring51.ConverterAndFormatter.EventFormatter@641856,java.lang.String -> me.soomin.demospring51.PropertyEditor.Event : me.soomin.demospring51.ConverterAndFormatter.EvnentConverter$StringToEventConverter@63f34b70
	java.nio.charset.Charset -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@4a31c2ee
	java.time.Duration -> java.lang.String : org.springframework.format.datetime.standard.DurationFormatter@74fef3f7
	java.time.Instant -> java.lang.Long : org.springframework.format.datetime.standard.DateTimeConverters$InstantToLongConverter@29a23c3d
	java.time.Instant -> java.lang.String : org.springframework.format.datetime.standard.InstantFormatter@5bb3131b
	java.time.LocalDateTime -> java.time.LocalDate : org.springframework.format.datetime.standard.DateTimeConverters$LocalDateTimeToLocalDateConverter@6719a5b8
	java.time.LocalDateTime -> java.time.LocalTime : org.springframework.format.datetime.standard.DateTimeConverters$LocalDateTimeToLocalTimeConverter@3eb631b8
	java.time.Month -> java.lang.String : org.springframework.format.datetime.standard.MonthFormatter@69eb86b4
	java.time.MonthDay -> java.lang.String : org.springframework.format.datetime.standard.MonthDayFormatter@5bb8f9e2
	java.time.OffsetDateTime -> java.time.Instant : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToInstantConverter@6999cd39
	java.time.OffsetDateTime -> java.time.LocalDate : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToLocalDateConverter@4d6f197e
	java.time.OffsetDateTime -> java.time.LocalDateTime : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToLocalDateTimeConverter@64e1dd11
	java.time.OffsetDateTime -> java.time.LocalTime : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToLocalTimeConverter@6ef7623
	java.time.OffsetDateTime -> java.time.ZonedDateTime : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToZonedDateTimeConverter@5c089b2f
	java.time.Period -> java.lang.String : org.springframework.format.datetime.standard.PeriodFormatter@54dcbb9f
	java.time.Year -> java.lang.String : org.springframework.format.datetime.standard.YearFormatter@2a037324
	java.time.YearMonth -> java.lang.String : org.springframework.format.datetime.standard.YearMonthFormatter@585ac855
	java.time.ZoneId -> java.util.TimeZone : org.springframework.core.convert.support.ZoneIdToTimeZoneConverter@53667cbe
	java.time.ZonedDateTime -> java.time.Instant : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToInstantConverter@5eccd3b9
	java.time.ZonedDateTime -> java.time.LocalDate : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToLocalDateConverter@796d3c9f
	java.time.ZonedDateTime -> java.time.LocalDateTime : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToLocalDateTimeConverter@41e1455d
	java.time.ZonedDateTime -> java.time.LocalTime : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToLocalTimeConverter@6bff19ff
	java.time.ZonedDateTime -> java.time.OffsetDateTime : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToOffsetDateTimeConverter@4e558728
	java.time.ZonedDateTime -> java.util.Calendar : org.springframework.core.convert.support.ZonedDateTimeToCalendarConverter@1d3e6d34
	java.util.Calendar -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToLongConverter@4642b71d,java.util.Calendar -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToLongConverter@1450078a
	java.util.Calendar -> java.time.Instant : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToInstantConverter@3122b117
	java.util.Calendar -> java.time.LocalDate : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToLocalDateConverter@66908383
	java.util.Calendar -> java.time.LocalDateTime : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToLocalDateTimeConverter@2bc12da
	java.util.Calendar -> java.time.LocalTime : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToLocalTimeConverter@41477a6d
	java.util.Calendar -> java.time.OffsetDateTime : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToOffsetDateTimeConverter@7ed9ae94
	java.util.Calendar -> java.time.ZonedDateTime : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToZonedDateTimeConverter@14bae047
	java.util.Calendar -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToDateConverter@2234078,java.util.Calendar -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToDateConverter@5ec77191
	java.util.Currency -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@22101c80
	java.util.Date -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$DateToLongConverter@4303b7f0,java.util.Date -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$DateToLongConverter@757529a4
	java.util.Date -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$DateToCalendarConverter@779de014,java.util.Date -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$DateToCalendarConverter@5c41d037
	java.util.Locale -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@34237b90
	java.util.Properties -> java.lang.String : org.springframework.core.convert.support.PropertiesToStringConverter@759d81f3
	java.util.UUID -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@5a4c638d
	me.soomin.demospring51.PropertyEditor.Event -> java.lang.String : me.soomin.demospring51.ConverterAndFormatter.EventFormatter@6a933be2,me.soomin.demospring51.PropertyEditor.Event -> java.lang.String : me.soomin.demospring51.ConverterAndFormatter.EventFormatter@641856,me.soomin.demospring51.PropertyEditor.Event -> java.lang.String : me.soomin.demospring51.ConverterAndFormatter.EvnentConverter$EventToStringConverter@5ebd56e9
	org.springframework.core.convert.support.ArrayToArrayConverter@267bbe1a
	org.springframework.core.convert.support.ArrayToCollectionConverter@13e698c7
	org.springframework.core.convert.support.ArrayToObjectConverter@29f0802c
	org.springframework.core.convert.support.ArrayToStringConverter@5a101b1c
	org.springframework.core.convert.support.ByteBufferConverter@e8fadb0
	org.springframework.core.convert.support.ByteBufferConverter@e8fadb0
	org.springframework.core.convert.support.ByteBufferConverter@e8fadb0
	org.springframework.core.convert.support.ByteBufferConverter@e8fadb0
	org.springframework.core.convert.support.CollectionToArrayConverter@aed0151
	org.springframework.core.convert.support.CollectionToCollectionConverter@1f12e153
	org.springframework.core.convert.support.CollectionToObjectConverter@6b410923
	org.springframework.core.convert.support.CollectionToStringConverter@60f2e0bd
	org.springframework.core.convert.support.FallbackObjectToStringConverter@464a4442
	org.springframework.core.convert.support.IdToEntityConverter@6eafb10e,org.springframework.core.convert.support.ObjectToObjectConverter@26a94fa5
	org.springframework.core.convert.support.MapToMapConverter@389562d6
	org.springframework.core.convert.support.ObjectToArrayConverter@3a60c416
	org.springframework.core.convert.support.ObjectToCollectionConverter@57bd2029
	org.springframework.core.convert.support.ObjectToOptionalConverter@2873d672
	org.springframework.core.convert.support.ObjectToOptionalConverter@2873d672
	org.springframework.core.convert.support.ObjectToOptionalConverter@2873d672
	org.springframework.core.convert.support.StreamConverter@203dd56b
	org.springframework.core.convert.support.StreamConverter@203dd56b
	org.springframework.core.convert.support.StreamConverter@203dd56b
	org.springframework.core.convert.support.StreamConverter@203dd56b
	org.springframework.core.convert.support.StringToArrayConverter@2160e52a

Web 환경에 적합한 것은 Formatter 라고 한다.

직접 구현하여 쓸일은 거의 없다고 한다. 부트에서 Test 환경을 가질 때,

 

@WebMvcTest는 웹과 관련된 빈만 등록해주는 것이다.

  1. 컨트롤러들이 주로 등록되며 Converter와 Formatter 빈이 제대로 등록되지않을경우 깨질 수 있다.
  2. 속성값에 테스트할 타입을 배열로 지정해주는게 명시적으로 좋다.
@RunWith(SpringRunner.class)
@WebMvcTest({EvnentConverter.EventToStringConverter.class,EventController.class})//웹과 관련된 빈만 등록하기에, Converter나 Formatter 를 등록하지 않을 경우 깨질 우려가 있다.
class EventControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void getTest() throws Exception {
        mockMvc.perform(get("/event/1"))
                .andExpect(status().isOk())
                .andExpect(content().string("1"));
    }

}