[Spring] 데이터 바인딩 추상화 : Converter와 Formatter
2022. 10. 30. 13:04ㆍJAVA/Spring
1. Converter 인터페이스
- PropertyEditor의 단점을 개선하는 인터페이스
- S 타입을 T 타입으로 변환할 수 있음
- 쓰레드 세이프함
PropertyEditor 단점
- Object와 String간의 변환만 할 수 있음
- 데이터 바인딩 간의 String 문자열이 아닌 타입과 타입간의 변환을 할 수 없음
PropertyEditor 메서드 호출
- getAsText : 어떤 객체를 String으로 변환하여 반환
- setAsText : 문자열을 입력받아 다른 객체로 변환하여 반환
2. Converter 인터페이스 구현 실습
- Converter 인터페이스를 구현하는 클래스 정의
- WebMvcConfigurer 인터페이스를 구현하는 웹 설정 클래스 구현
- addFormatters 메서드 재정의
- FormatterRegistry 인스턴스를 이용하여 Converter 구현 인스턴스를 등록
- 문자열이 인스턴스로 변환이 되는지 테스트 코드 추가
1. EventConverter 클래스를 정의하여 Event 클래스 타입에 대한 Source Converter와 Target Converter를 구현합니다.
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();
}
}
}
- StringToEventConveter 클래스 : 문자열을 Event 클래스 타입으로 변환
- EventToStringConveter 클래스 : Event 인스턴스를 문자열로 변환
- 2개의 Converter 구현 클래스가 하나의 PropertyEditor를 대체
2. SpringBoot없이 SpringMVC를 사용하는 경우 Converter 등록하기
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new EventConverter.StringToEventConverter());
}
}
3. 테스트 코드를 작성합니다.
@WebMvcTest
public class EventControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void getTest() throws Exception {
//given
String url = "/event/";
String id = "1";
//when
String actual = mockMvc.perform(get(url+id))
.andExpect(status().isOk())
.andExpect(content().string(id))
.andReturn().getResponse().getContentAsString();
//then
assertThat(actual).isEqualTo("1");
}
}
- 실행 결과 url의 문자열 “1”이 Event 타입으로 변환한 다음에 컨트롤러에서 id 문자열을 반환하는 것을 확인할 수 있습니다.
3. Formatter 인터페이스
- PropertyEditor 대체제
- Spring이 제공하는 웹 쪽에 특화되어 있는 인터페이스
- Object와 String 간의 변환을 담당한다.
- FormatterRegistry에 등록해서 사용
Converter 인터페이스와 Formatter 인터페이스의 차이
- Formatter 인터페이스는 문자열을 Locale에 따라 다국화하는 기능을 제공 (선택)
4. Formatter 인터페이스 구현 실습
- Formatter 인터페이스 구현 클래스 빈 정의
- 테스트 코드 추가
1. Formatter 인터페이스를 구현할 클래스 정의
@Component
@AllArgsConstructor
public class EventFormatter implements Formatter<Event> {
private final 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", null, locale);
return object.getId().toString();
}
}
- Locale 정보를 기반으로 문자열 변환 가능 (선택)
- 쓰레드 세이프하기 때문에 빈으로 등록해서 사용이 가능함
- 빈으로 등록이 가능하기 때문에 의존 객체(필드 멤버)에 자동 주입해서 사용이 가능함
2. Formatter 인터페이스 구현 클래스를 등록할 웹 설정 클래스 정의
@Configuration
@AllArgsConstructor
public class WebConfig implements WebMvcConfigurer {
private final MessageSource messageSource;
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new EventFormatter(messageSource));
}
}
3. 테스트 코드를 작성 및 실행
@WebMvcTest
public class EventControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void getTest() throws Exception {
//given
String url = "/event/";
String id = "1";
//when
String actual = mockMvc.perform(get(url+id))
.andExpect(status().isOk())
.andExpect(content().string(id))
.andReturn().getResponse().getContentAsString();
//then
assertThat(actual).isEqualTo("1");
}
}
5. ConversionService
- 실제 변환 작업은 이 인터페이스를 통해서 쓰레드-세이프하게 수행됨
- 스프링 MVC, 빈 설정, SpEL에서 사용됨
- DefaultFomrattingConversionService 클래스
- ConversionService 인터페이스 구현체
- FormatterRegistry, ConversionService 기능 수행
- 여러 기본 Converter와 Formatter 구현 클래스 빈들을 자동 등록해줌
- ConversionService를 사용하면 WebMvcConfigure 인터페이스 구현 클래스를 정의하고 addFormatters 메서드를 재정의하여 등록하지 않아도됨
- 빈으로 등록된 Converter, Formatter 인터페이스 구현 클래스를 스프링 프레임워크가 자동으로 등록해줌
6. ConversionService 실습
실습1 : 자동주입된 ConversionService 인스턴스의 타입을 확인
1. ConversionService의 타입이 무엇인지 테스트 코드를 추가합니다.
@SpringBootTest
public class ConversionServiceTest {
@Autowired
ConversionService conversionService;
@Test
public void webConversionServiceClassTest(){
//given
//when
boolean actual = conversionService instanceof WebConversionService;
//then
assertThat(actual).isTrue();
}
}
- 실행결과 : ConversionService가 WebConversionService로 주입됨
- WebConversionService 클래스가 스프링부트가 기본적으로 제공하는 구현 클래스
- WebConversionService 클래스는 DefaultFormattingConversionService 클래스를 상속하여 구현한 클래스
- DefaultFormattingConversionService 클래스보다 조금더 많은 기능을 가짐
- ConversionService 인스턴스의 convert 메서드를 통하여 변환할 수 있지만 ConversionService 의존 객체를 직접 주입하여 사용하지는 않음
실습 2 : FormatterRegistry에 Converter 또는 Formatter를 명시적으로 등록하지 않고 ConversionService가 Converter, Formatter 빈을 자동 등록했고 변환이 되는지 테스트
- Formatter 인터페이스 구현 클래스 빈 정의
- 빈으로 등록된 구현 클래스는 ConversionService에 의해서 자동 등록됨
- 테스트코드 추가
1. 테스트 코드를 추가합니다.
@WebMvcTest({EventFormatter.class, EventController.class})
public class EventControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void getTest() throws Exception {
//given
String url = "/event/";
String id = "1";
//when
String actual = mockMvc.perform(get(url+id))
.andExpect(status().isOk())
.andExpect(content().string(id))
.andReturn().getResponse().getContentAsString();
//then
assertThat(actual).isEqualTo("1");
}
}
2. Event 클래스의 Formatter를 구현하고 빈으로 등록
@Component
@AllArgsConstructor
public class EventFormatter implements Formatter<Event> {
private final 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", null, locale);
return object.getId().toString();
}
}
3. 테스트 코드를 실행하여 결과를 확인
Event(id=1)
- 실행 결과 : FormatterRegistry에 명시적으로 등록하지 않음에도 문자열 “1”이 Event 타입으로 변환된 것을 확인함
정리
- Converter와 Formatter를 이용하여 PropertyEditor를 대체한 타입 변환을 정의할 수 있음
- Spring Web MVC를 사용하는 경우 Formatter를 사용하는 것을 권장함
- ConversionService는 Convert와 Formatter 구현 클래스 빈을 자동 등록하여 따로 등록할 필요없이 변환 서비스를 지원하게 해줌
References
source code : https://github.com/yonghwankim-dev/spring_study/tree/master/spring_converter_formatter/src/main/java/kr/yh
[인프런] 스프링 프레임워크 핵심 기술
'JAVA > Spring' 카테고리의 다른 글
[Spring] Spring AOP(Aspect-Oriented Programming) 개념 (0) | 2022.10.30 |
---|---|
[Spring] Spring Expression Language (0) | 2022.10.30 |
[Spring] 데이터 바인딩 추상화 : PropertyEditor (0) | 2022.10.28 |
[Spring] Validation 추상화 (0) | 2022.10.28 |
[Springboot] 24. Externalized Configuration #2 @ConfigurationProperties (0) | 2022.10.28 |