데이터 바인딩 추상화: Converter와 Formatter

맹기·2021년 4월 9일
0

Spring 핵심 기술 원리

목록 보기
15/19

1. Converter 등록

  • EventConverter.java
public class EventConverter {

    public static class StringToEventConverter implements Converter<String, Event> {
        @Override
        public Event convert(String source) {
            return new Event(Integer.parseInt(source));
        }
    }

    public static class EventToStringConverter implements Converter<Event, String> {
        @Override
        public String convert(Event source) {
            return source.getId().toString();
        }
    }
}
  • WebConfig.java
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new EventConverter.StringToEventConverter());
    }
}

위의 두개의 클래스를 생성하면 아래의 테스트에서 Converter가 등록되어 String > Event 형변환이 자동으로 되어 테스트에 통과한다.

  • EventController.java
@RestController
public class EventController {
    @GetMapping("/event/{event}")
    public String getEvent(@PathVariable Event event) {
        System.out.println(event);
        return event.getId().toString();
    }
}
  • EventControllerTest.java
@RunWith(SpringRunner.class)
@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"));
    }
}

2. Formatter 등록

  • EventFormatter.java
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 event, Locale locale) {
        return event.getId().toString();
    }
}
  • WebConfig.java
@Configuration
public class WebConfig implements WebMvcConfigurer {

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

위의 Converter와 Formatter의 인터페이스를 사용하면 ConversionService에 등록이 되어 실제 변환 작업은 ConversionService를 이용하여 실제 변환 작업이 이루어진다.

3. 스프링 부트에서의 사용법

스프링 부트에서는 FormatterRegistry를 따로 등록하지 않아도, Formatter 또는 Converter를 Bean으로 등록만 해주면 자동으로 사용 가능하다.

  • WebConfig.java 삭제 후 아래의 Formatter와 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();
        }
    }
}
package me.hyunki.demospring51;

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> {

    @Override
    public Event parse(String text, Locale locale) throws ParseException {
        return new Event(Integer.parseInt(text));
    }

    @Override
    public String print(Event event, Locale locale) {
        return event.getId().toString();
    }
}
  • localhost:8080/event/11 실행 결과

4.@WebMvcTest

web관련 test, 테스트가 깨질 우려가 있을 경우 빈으로 등록 가능함
해당 어노테이션을 사용하여 테스트할 빈을 명시적으로 넣어주는것도 좋은 방법임

@RunWith(SpringRunner.class)
@WebMvcTest({
        EventFormatter.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"));
    }
}

대부분 Converter보다는 Formatter를 많이사용하고 Web 관련해서는 Formatter 쓰는게 좋음.

5. 등록되어있는 Convert들을 전부 보는 방법

System.out.println(conversionService); 활용하여 볼수 있으며,
콘솔창에 엄청많은 converter들이 보여진다.

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

6. 강의노트 정리

  • Converter
    • S 타입을 T 타입으로 변환할 수 있는 매우 일반적인 변환기.
    • 상태 정보 없음 == Stateless == 쓰레드세이프
    • ConverterRegistry에 등록해서 사용
public class StringToEventConverter implements Converter<String, Event> {
	@Override
    public Event convert(String source) {
    	Event event = new Event();
        event.setId(Integer.parseInt(source));
        return event;
    }
}
  • Formatter
    • PropertyEditor 대체제
    • Object와 String 간의 변환을 담당한다.
    • 문자열을 Locale에 따라 다국화하는 기능도 제공한다. (optional)
    • FormatterRegistry에 등록해서 사용.
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 event, Locale locale) {
        return event.getId().toString();
    }
}
  • ConversionService
    • 실제 변환 작업은 이 인터페이스를 통해서 쓰레드-세이프하게 사용할 수 있음.
    • 스프링 MVC, 빈 (value) 설정, SpEL에서 사용한다.
    • DefaultFormattingConversionService
      • FormatterRegistry
      • ConversionService
      • 여러 기본 컨버터와 포매터 등록 해 줌.

  • 스프링 부트
    • 웹 애플리케이션인 경우에 DefaultFormattingConversionService를 상속하여 만든 WebConversionService를 빈으로 등록해 준다.
    • Formatter와 Converter 빈을 찾아 자동으로 등록해 준다.

0개의 댓글

관련 채용 정보