Spring/Spring MVC

Spring MVC - Formatter 구현 및 사용 ( Formatter와 FormattingConversionService )

jddng 2022. 2. 17. 11:48
728x90
반응형

 

 

 

 

스프링에 Formatter 적용하기


 

 

 

 

 

 

Formatter

 Formatter는 문자에 특화된 타입 변환 Converter의 특별한 버전이다. 객체와 문자, 문자와 객체의 타입 변환시 특정 포멧으로 Locale 정보에 따라 문자를 출력하거나 또는 그 반대의 역할을 하는 특화된 기능이 포함된 인터페이스이다.

 

 Converter는 타입 변환이 범용적으로 쓰이는 타입 변환기라고 볼 수 있으며 Formatter는 문자에 특화된 타입 변환기라고 볼 수 있다.

 

ex) 날짜 객체 -> 2022-03-02 16:14:00

 

 

참고
@RequestParam, @ModelAttribute, @PathVariable 등에서 사용 가능
메시지 컨버터(JSON)에는 컨버전 서비스가 적용되지 않는다.

 

 

Formatter 인터페이스

 

  • Formatter 인터페이스는 Printer 인터페이스와 Parser 인터페이스를 상속 받는다.
  • Printer<T>
           - 객체를 문자로 변경하는 역할
  • Parser<T>
           - 문자를 객체로 변경하는 역할

 

@FunctionalInterface
public interface Printer<T> {
	String print(T object, Locale locale);
}

@FunctionalInterface
public interface Parser<T> {
	T parse(String text, Locale locale) throws ParseException;
}

public interface Formatter<T> extends Printer<T>, Parser<T> {
}

 

Formatter 구현

 

 간단하게 1000 단위로 쉼표가 들어가는 포맷이 적용되는 포맷터를 구현해보자.

 

@Slf4j
public class MyNumberOfFormatter implements Formatter<Number> {

    @Override
    public Number parse(String text, Locale locale) throws ParseException {

        log.info("text={}, locale={}", text, locale);
        NumberFormat format = NumberFormat.getInstance(locale);
        return format.parse(text);
    }

    @Override
    public String print(Number object, Locale locale) {

        log.info("object={}, locale={}", object, locale);
        NumberFormat instance = NumberFormat.getInstance(locale);
        String format = instance.format(object);
        return format;
    }
}

 

 위에서 구현한 포맷터를 테스트를 해보면 타입 변환과 포맷이 잘 이루어진 것을 볼 수 있다.

 

    @Test
    void parse() throws ParseException {
        MyNumberOfFormatter formatter = new MyNumberOfFormatter();

        Number result = formatter.parse("1,000", Locale.KOREA);
        Assertions.assertThat(result).isEqualTo(1000L);
    }

    @Test
    void print() {
        MyNumberOfFormatter formatter = new MyNumberOfFormatter();

        String result = formatter.print(1000, Locale.KOREA);
        Assertions.assertThat(result).isEqualTo("1,000");
    }

 

FormattingConversionService

 포맷터 역시 여러 포맷터가 있을 경우 관리 및 사용이 불편하기 때문에 스프링에서 포맷터를 모아서 편리하게 사용할 수 있는 기능을 제공하는데 그것이 바로 FormattingConversionService 이다. 

 

 포맷터도 타입 변환 기능에 포맷 기능이 추가된 특별한 컨버터 이므로 FormattingConversionService 내부를 보면 ConversionService을 구현하고 있다는 것을 알 수 있다.

 

 

DefaultFormattingConversionService 사용

 

  • 컨버터 등록 및 사용과 포맷터 등록 및 사용 모두 구현한 클래스
  • addConverter()
            - 컨버터와 포맷터 등록
  • convert()
            - 컨버터와 포맷터 사용

 

 위에서 예시로 만든 포맷터를 등록하고 사용하는 테스트 코드를 구현해보자.

 

public class ConversionServiceTest {

    @Test
    void conversionService(){

        // 컨버전 등록
        DefaultConversionService conversion = new DefaultConversionService();
        conversion.addConverter(new StringToIntegerConverter());
        conversion.addConverter(new StringToIpPortConverter());
        conversion.addConverter(new StringToIpPortConverter());
        conversion.addConverter(new IpPortToStringConverter());


        // 컨버전 사용
        Assertions.assertThat(conversion.convert("10", Integer.class)).isEqualTo(10);
        Assertions.assertThat(conversion.convert(10, String.class)).isEqualTo("10");

        IpPort ipPort = conversion.convert("127.1.2.3:8080", IpPort.class);
        Assertions.assertThat(ipPort).isEqualTo(new IpPort("127.1.2.3", 8080));

        String ipPortString = conversion.convert(new IpPort("127.0.0.1", 8080), String.class);
        Assertions.assertThat(ipPortString).isEqualTo("127.0.0.1:8080");
    }

}

 

 DefaultFormattingConversionService 를 이용하면 컨버터뿐만아니라 포맷터까지 등록 및 사용이 가능하다는 것을 알 수 있다. 아래는 DefaultFormattingConversionService 의 상속 구조이다.

 


 

스프링에 Formatter 등록하고 사용하기

 

Formatter 등록

 

 

  • Formatter를 등록하기 위해서는 WebMvcConfigurer가 제공하는 addFormatters()메서드를
    사용하여 Formatter를 등록할 수 있다.
  • addFormatters(FormatterRegistry registry)
         - FormatterRegistry : 인터페이스 ConveterRegistry의 구현 클래스
  • 참고로 위에서 만든 컨버터(String→Integer)를 등록하면 기본 컨버터가 아닌
    등록한 컨버터가 호출된다.

 

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new IpPortToStringConverter());

        registry.addFormatter(new MyNumberOfFormatter());
    }
}

 

등록한 Formatter 사용

 

 위에서 만든 포맷터인 MyNumberOfFormatter를 사용해보자.

 

    @GetMapping("/converter")
    public String converterV1(@RequestParam Integer data){
        log.info("data = {}", data);
        return "ok";
    }

 

/converter?data=10,000 요청시 전달된 파라미터 문자열이 포맷이 적용된 타입 변환이 잘 이루어진 것을 볼 수있다.


 

스프링이 제공하는 기본 Formatter

 포맷터는 기본형식이 지정되어 있기 때문에, 객체의 각 필드마다 다른 형식으로 포맷을 지정하기는 어렵다. 스프링에서는 이런 문제를 해결하기 위해 애노테이션 기반으로 원하는 형식을 지정해서 사용할 수 있는 매우 유용한 포맷터 두 가지를 기본으로 제공한다

 

  • @NumberFormat : 숫자 관련 형식 지정 포맷터 사용
  • @DateTimeFormat : 날짜 관련 형식 지정 포맷터 사용

 

    @Data
    static class Form {
        @NumberFormat(pattern = "###,###")
        private Integer number;
        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private LocalDateTime localDateTime;
    }

 

 해당 클래스 필드에 넘어온 값이 바인딩 될 때  각 필드에 지정된 포맷애노테이션에 따라 포맷터가 적용이 된다.

728x90
반응형