[Spring] 컨버터와 포맷터 (컨버전 서비스, 스프링 기본 제공 포맷터)

728x90

 

컨버터

새로운 타입을 만들어 변환하고 싶을 때, 컨버터를 만들어 등록하면 쉽게 사용할 수 있다.

 

컨버터 인터페이스

public interface Converter<S, T> {
  T convert(S source);
}

S 타입에서 T 타입으로 변환하는 것이다.

 

> 참고 <

org.springframework.core.convert.converter 를 사용해야 한다.

 

사용 예시

@Getter
@EqualsAndHashCode
public class IpPort {
  private String ip;
  private int port;
  
  public IpPort(String ip, int port) {
    this.ip = ip;
    this.port = port;
  }
}
public class IpPortToStringConverter implements Converter<IpPort, String> {
  @Override
  public String convert(IpPort source) {
    return source.getIp() + ":" + source.getPort();
  }
}
public class StringToIpPortConverter implements Converter<String, IpPort> {
  @Override
  public IpPort convert(String source) {
    // "127.0.0.1:8080"
    String[] split = source.split(":");
    String ip = split[0];
    int port = Integer.parseInt(split[1]);
    return new IpPort(ip, port);
  }
}

IpPort 객체를 String 으로 변환하는 컨버터와,

String 을 IpPort 객체로 변환하는 컨버터이다.

 

컨버전 서비스

ConversionService 는 개별 컨버터를 묶어서 편리하게 사용할 수 있는 기능을 제공한다.

 

DefaultConversionService

ConversionService : 컨버터 사용

ConversionRegistry : 컨버터 등록

두 인터페이스를 구현하였다.

 

사용 방법

DefaultConversionService conversionService = new DefaultConversionService();

conversionService.addConverter(new StringToIpPortConverter());
conversionService.addConverter(new IpPortToStringConverter());

assertThat(conversionService.convert("127.0.0.1:8080", IpPort.class))
                            .isEqualTo(new IpPort("127.0.0.1", 8080));
assertThat(conversionService.convert(new IpPort("127.0.0.1", 8080), String.class))
                            .isEqualTo("127.0.0.1:8080");

 

 

컨버터 등록 시에는 타입 컨버터를 명확히 알아야 하지만,

컨버터 사용 시에는 타입 컨버터를 모른 채로 convert() 를 사용하면 된다.

 

WebMvcConfigurer

스프링은 내부에서 ConversionService 를  제공한다.

 

WebMvcConfigurer 가 제공하는 addFormatters() 를 사용해서 추가하고 싶은 컨버터를 등록하면,

스프링이 ConversionService 에 컨버터를 추가해 준다.

 

포맷터

객체를 특정 포맷에 맞추어 문자로 출력할 때(또는 그 반대) 특화된 기능이 포맷터이다.

 

포맷터 인터페이스

public interface Printer<T> {
String print(T object, Locale locale);
}
public interface Parser<T> {
T parse(String text, Locale locale) throws ParseException;
}
public interface Formatter<T> extends Printer<T>, Parser<T> {
}

print() : 객체를 문자로 변경

parse() : 문자를 객체로 변경

 

사용 예시

public class MyNumberFormatter implements Formatter<Number> {
  @Override
  public Number parse(String text, Locale locale) throws ParseException {
    NumberFormat format = NumberFormat.getInstance(locale);
    return format.parse(text);
  }
 
  @Override
  public String print(Number object, Locale locale) {
    return NumberFormat.getInstance(locale).format(object);
  }
}

NumberFormat 은 숫자 중간에 가독성을 위한 쉼표를 적용해 주는 포맷터로 자바가 기본으로 제공하는 객체이다.

여기서 Locale 정보를 통해 나라별로 다른 숫자 포맷을 만들어 준다.

 

 

> 참고 <

Number 타입은 Integer, Long 같은 숫자 타입의 부모 클래스이다.

 

포맷터 지원 컨버전 서비스

내부에서 어댑터 패턴을 사용하여 포맷터도 컨버터처럼 동작하도록 지원한다.

 

DefaultFormattingConversionService

FormattingConversionService : 포맷터를 지원하는 컨버전 서비스 (ConversionService 관련 기능 상속받음)

+ 기본적인 통화, 숫자 관련 기본 포맷터를 추가하여 제공

 

DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();

conversionService.addConverter(new StringToIpPortConverter());
conversionService.addConverter(new IpPortToStringConverter());
conversionService.addFormatter(new MyNumberFormatter());

assertThat(conversionService.convert("127.0.0.1:8080", IpPort.class))
                                       .isEqualTo(new IpPort("127.0.0.1", 8080));
assertThat(conversionService.convert(1000, String.class)).isEqualTo("1,000");
assertThat(conversionService.convert("1,000", Long.class)).isEqualTo(1000L);

 

WebConversionService

스프링 부트는 내부에서 DefaultFormattingConversionService 를 상속받은 WebConversionService 를 사용한다.

 

스프링 제공 기본 포맷터

스프링은 애노테이션 기반으로 원하는 형식을 지정해서 사용할 수 있는 포맷터를 제공한다.

 

@NumberFormat : 숫자 관련 형식 포맷터 사용 (NumberFormatAnnotationFormatterFactory 사용됨)

@DataTimeFormat : 날짜 관련 형식 지정 포맷터 사용 (Jsr310DateTimeFormatAnnotationFormatterFactory 사용됨)

 

@Data
class Form {

  @NumberFormat(pattern = "###,###")
  private Integer number;

  @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime localDateTime;
}

 

타임리프 사용

*{{...}} : 컨버전 서비스를 적용하여 변환된 결과로 조회

th:field="${...}" : value 속성이 컨버젼 서비스를 적용하여 변환된 값을 갖는다.

 

> 참고 <

HttpMessageConverter 는

HTTP 메시지 바디 내용을 객체로 변환하거나 객체를 HTTP 메시지 바디에 입력하는 것이기 때문에

컨버전 서비스가 적용되지 않는다.

컨버전 서비스 @RequestParam, @ModelAttribute, @PathVariable, 뷰 템플릿

메시지 컨버터 @RequestBody, @ResponseBody, HttpEntity, ResponseEntity

 

 

 

 

 

 

 

 

 

 

 

출처 | 스프링 MVC 2(김영한) - 인프런

 
728x90