HTTP 요청의 파라미터는 모두 문자열로 처리가 된다. 만약 숫자를 사용하고 싶다? 그렇다면 이렇게 타입을 변환해주어야 한다.
@GetMapping("/hello-v2")
public String helloV2(@RequestParam Integer data) {
System.out.println("data = " + data);
return "ok";
}
- HTTP 쿼리 스트링으로 전달하는 data=10 부분에서 10d은?
-> 숫자 10(x), 문자 10(ㅇ)- 스프링이 제공하는
@RequestParam
을 사용하면 이 문자 10을 Integer 타입의 숫자 10으로 편리하게 받을 수 있다.
=> 이것은 스프링이 중간에서 타입을 변환해주었기 때문이다. 😁✌
=>@ModelAttribute
,@PathVariable
에서도 도움을 받을 수 있다.
[타입 컨버터를 사용하려면 ]
org.springframework.core.convert.converter.Converter
인터페이스를 구현하면 된다
타입 컨버터 이해를 돕기 위해 조금 다른 컨버터를 준비해보았다.
127.0.0.1:8080
과 같은 IP, PORT를 입력하면 IpPort 객체로 변환하는 컨버터를 만들어보자.
@Getter
@EqualsAndHashCode
public class IpPort { //사용자 정의 타입 컨버터
private String ip;
private int port;
public IpPort(String ip, int port) {
this.ip = ip;
this.port = port;
}
}
이렇게 타입 컨버터를 하나하나 직접 찾아서 타입 변환에 사용하는 것은 매우 불편하다.
- '스프링'은 용도에 따라 다양한 방식의 타입 컨버터를 제공한다.
- 그래서 스프링은 '개별 컨버터'를 모아두고 그것들을 묶어서 편리하게 사용할 수 있는 기능을 제공하는데,
=> 이것이 바로 '컨버전 서비스( ConversionService )'이다.
package org.springframework.core.convert;
import org.springframework.lang.Nullable;
public interface ConversionService {
boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor
targetType);
<T> T convert(@Nullable Object source, Class<T> targetType);
Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType,
TypeDescriptor targetType);
}
컨버전 서비스 인터페이스는 단순히 1. 컨버팅이 가능한가? 확인하는 기능과, 2.컨버팅 기능을 제공한다.
@Configuration
를 통해 스프링 ConversionService
에 등록하고, 사용하면 된다.str->Integer
)을 한다면, 아래와 같이 직접 추가한 컨버터를 먼저 사용한다.[처리과정]
@RequestParam
을 처리하는 ArgumentResolver 인 RequestParamMethodArgumentResolver
에서 ConversionService
를 사용해서 타입을 변환한다.${{...}}
${...}
${{...}}
th:field
는 자동으로 스프링 컨버터를 적용해서 HTTP 요청메시지를 보낸다.
- '포맷터'를 지원하는 컨버전 서비스를 사용하면 컨버전 서비스에 포맷터를 추가할 수 있다.
-> (내부) '어댑터 패턴'을 사용해서Formatter
가Converter
처럼 동작하도록 지원FormattingConversionService
는 포맷터를 _지원하는 컨버전 서비스_이다.
->DefaultFormattingConversionService
는FormattingConversionService
에 기본적인 통화, 숫자 관련 몇가지 기본 포맷터를 추가해서 제공한다.
public class FormattingConversionServiceTest {
@Test
void formattingConversionService() {
DefaultFormattingConversionService conversionService = new
DefaultFormattingConversionService();
//컨버터 등록
conversionService.addConverter(new StringToIpPortConverter());
conversionService.addConverter(new IpPortToStringConverter());
//포맷터 등록
conversionService.addFormatter(new MyNumberFormatter());
//컨버터 사용
IpPort ipPort = conversionService.convert("127.0.0.1:8080",
IpPort.class);
assertThat(ipPort).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);
}
}
이후 WebConfig에서 포맷터까지 적용해주어야 한다!
StringToIntegerConverter
, IntegerToStringConverter
를 꼭 주석처리스프링은 자바에서 기본으로 제공하는 타입들에 대해 수 많은 포맷터를 기본으로 제공한다.
물론 나만의 포맷터가 필요하다면 이렇게 만들어서 등록하면 된다.
그러나 IDE에서 'Formatter 인터페이스의 구현 클래스'를 찾아보면 수 많은 '날짜' or '시간' 관련 포맷터가 제공되는 것을 확인할 수 있다.
but... '포맷터'는 기본 형식이 지정되어 있기 때문에, 객체의 각 필드마다 다른 형식으로 포맷을 지정하기는 어렵다.
.
.
스프링은 이런 문제를 해결하기 위해
@NumberFormat
: 숫자 관련 형식 지정 포맷터 사용 - NumberFormatAnnotationFormatterFactory
@DateTimeFormat
: 날짜 관련 형식 지정 포맷터 사용Jsr310DateTimeFormatAnnotationFormatterFactory
• ${form.number}: 10000
• ${{form.number}}: 10,000
• ${form.localDateTime}: 2021-01-01T00:00:00
• ${{form.localDateTime}}: 2021-01-01 00:00:00
.
.
컨버터를 사용하든, 포맷터를 사용하든 등록 방법은 다르지만, '사용할 때'는 컨버전 서비스를 통해서 일관성 있게 사용할 수 있다
- 컨버전 서비스는
@RequestParam
,@ModelAttribute
,@PathVariable
, '뷰 템플릿' 등에서 사용할 수 있다 ✨👍