URL경로는 항상 문자이다.
parameter로 data=10이 들어온다면, 이는 String형태이고, 이를 숫자로 받으려면 유저가 내부에서 변환을 해야한다.
@GetMapping("/hello-v1")
public String helloV1(HttpServletRequest request) {
String data = request.getParameter("data");
Integer intValue = Integer.valueOf(data);
System.out.println("intValue = " + intValue);
return "ok";
}
혹은 RequestParam으로 변환할 수도 있다.
@GetMapping("/hello-v2")
public String helloV2(@RequestParam Integer data) {
System.out.println("data = " + data);
return "ok";
}
이는 스프링이 컨버터 기능을 지원해서 자동변환 해주기 때문이다. 만약 개발자가 자신이 타입을 만들어서 변환하고 싶다면 어떻게 해야할까?
이때를 위해서 컨버터 인테페이스가 존재한다.
package org.springframework.core.convert.converter;
public interface Converter<S, T> {
T convert(S source);
}
@Slf4j
public class StringToIntegerConverter implements Converter<String, Integer> {
@Override
public Integer convert(String source) {
log.info("convert source={}", source);
return Integer.valueOf(source);
}
}
@Slf4j
public class IntegerToStringConverter implements Converter<Integer, String> {
@Override
public String convert(Integer source) {
log.info("convert source={}", source);
return String.valueOf(source);
}
}
이렇게 Converter를 받아서 사용할 수 있다.
IpPort라는 내가 직접 만든 Type을 String을 변환 할 수 있다.
> Converter 기본 타입 컨버터
> ConverterFactory 전체 클래스 계층 구조가 필요할 때
> GenericConverter 정교한 구현, 대상 필드의 애노테이션 정보 사용 가능
> ConditionalGenericConverter 특정 조건이 참인 경우에만 실행
컨버터는 이처럼 다양한 기능을 제공한다.
하지만 이를 직접 사용하는건 불편하다. 따라서 이를 묶어서 사용하는 Conversion 서비스에 대해 알아보겠다.
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToIntegerConverter());
conversionService.addConverter(new IntegerToStringConverter());
conversionService.addConverter(new StringToIpPortConverter());
conversionService.addConverter(new IpPortToStringConverter());
이런식으로 컨버전 서비스를 등록해놓고
conversionService.convert("10",Integer.class)
이런식으로 사용하면 알아서 conversionService가 찾아서 해결해준다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToIntegerConverter());
registry.addConverter(new IntegerToStringConverter());
registry.addConverter(new StringToIpPortConverter());
registry.addConverter(new IpPortToStringConverter());
}
}
이제 아까만든 controller를 써보면
이렇게 잘 작동되는것을 알 수 있다. 하지만 이는 아까도 잘 작동했다. 이는 스프링이 기본적으로 컨버터를 제공해주기 때문이다.
http://localhost:8080/ip-port?ipPort=127.0.0.1:8080
@GetMapping("/ip-port")
public String IpPort(@RequestParam IpPort ipPort) {
System.out.println("ipPort IP= " + ipPort.getIp());
System.out.println("ipPort Port= " + ipPort.getPort());
return "ok";
}
이 컨트롤러를 실행해보면
이렇게 잘 작동한다.
또한 View에서의 컨버팅도 잘 작동한다.
컨버터는 입력과 출력 타입에 제한이 없는 변환기능을 제공한다.
만약 1000 이라는 문자를 1,000이라는 현금 단위로 바꾸거나, 날짜를 포맷에 맞춰서 출력할 때 어떻게 해야할까?
이때 사용하는 기능이 Formatter이다.
포매터에는 출력담당의 프린터와 변환담당의 파서 두 기능이 있다. 이를 모두 구현해야한다.
@Slf4j
public class MyNumberFormatter implements Formatter {
@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(Object object, Locale locale) {
log.info("object={}, locale={}", object, locale);
return NumberFormat.getInstance(locale).format(object);
}
}
각 로케일에 맞춰서 미리 스프링이 구현해둔 방식으로 변환해준다.
FormattingConversionService 는 포맷터를 지원하는 컨버전 서비스이다.
DefaultFormattingConversionService 는 FormattingConversionService 에 기본적인 통화, 숫자 관련 몇가지 기본 포맷터를 추가해서 제공한다.
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
conversionService.addConverter(new StringToIpPortConverter());
conversionService.addConverter(new IpPortToStringConverter());
conversionService.addFormatter(new MyNumberFormatter());
DefaultConversionService는 포매터는 물론 컨버터도 지원한다. 따라서 통합해서 사용하면 된다.
registry.addConverter(new StringToIpPortConverter());
registry.addConverter(new IpPortToStringConverter());
registry.addFormatter(new MyNumberFormatter());
WebConfig에는 이런식으로 추가하면, 정상적으로 NumberFormatter가 작동한다.
스프링은 많은 기본 포메터를 제공하고, 동시에 어노테이션으로 포맷을 지정해줄 수 있는 포메터를 제공해준다.
@Data
static class Form {
@NumberFormat(pattern = "###,###")
private Integer number;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime;
}
annotation으로 값을 주면, 해당 양식에 맞춰서 formatting이 작동하게 된다.