스프링 타입 컨버터
- 기본적으로 HTTP 요청 파라미터(HTTP 쿼리 스트링)는 모두 문자로 처리된다.
- 다른 타입으로 변환해서 사용하고 싶으면 변환 과정을 거쳐야 한다.
- 문자를 정수로 변환 ex) Integer.valueOf(data)
- 스프링이 제공하는 @RequestParam, @ModelAttribute, @PathVariable 등을 사용하면 편리하게 타입 변환을 할 수 있다.
- ex) public String hello(@ReqeustParam("data") Integer data){}
- 다른 타입으로 변환해서 사용하고 싶으면 변환 과정을 거쳐야 한다.
- 스프링에 추가적인 타입 변환이 필요한 경우 스프링에서 제공하는 확장 가능한 org.springframework.core.convert.Converter 인터페이스를 구현해서 등록하면 된다.
- 컨버터 인터페이스는 모든 타입에 적용할 수 있다.
public interface Converter<S, T> {
@Nullable
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);
}
}
- Converter 인터페이스 구현 예제 String -> Integer로 변환
- 스프링은 용도에 따라 다양한 방식의 타입 컨버터를 제공한다.
- Converter : 기본 타입 컨버터
- ConverterFactory : 전체 클래스 계층 구조가 필요할 때
- GenericConverter : 정교한 구현, 대상 필드의 어노테이션 정보 사용 가능
- ConditionalGenericConverter : 특정 조건이 참인 경우에만 실행
ConversionService
- 구현한 타입 컨버터를 하나하나 직접 찾아서 변환에 사용하는 것은 매우 불편하다.
- ConversionService : 스프링에서 개별 컨버터를 모아두고 묶어서 편리하게 사용할 수 있도록 제공하는 기능이다.
- canConvert() : 컨버팅이 가능한지 여부 확인
- convert() : 컨버팅 기능
//등록
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToIntegerConverter());
conversionService.addConverter(new IntegerToStringConverter());
conversionService.addConverter(new StringToIpPortConverter());
conversionService.addConverter(new IpPortToStringConverter());
//사용
assertThat(conversionService.convert("10", Integer.class)).isEqualTo(10);
assertThat(conversionService.convert(10, String.class)).isEqualTo("10");
IpPort ipPort = conversionService.convert("127.0.0.1:8080", IpPort.class);
assertThat(ipPort).isEqualTo(new IpPort("127.0.0.1", 8080));
String ipPortString = conversionService.convert(new IpPort("127.0.0.1", 8080), String.class);
assertThat(ipPortString).isEqualTo("127.0.0.1:8080");
- ConversionService 사용 예제
- Convert를 구현한 컨버터를 등록하고 사용할 수 있다.
- 사용할 때는 변환할 값과 타입을 지정하면 등록된 컨버터 중 적절한 컨터버를 찾아서 변환해준다.
- 실제로 사용할 때는 ConversionService에 컨버터를 등록하는 부분과 사용하는 부분을 따로 분리해야 한다.
- 의존관계 주입을 통해서 사용해야 한다.
- 사용자 입장에서는 타입 컨버터를 몰라도 되기에 사용하는 부분에만 의존하면 된다.
- ISP 인터페이스 분리 원칙에 따라서 클라이언트가 자신이 이용하지 않는 메소드에 의존하지 않도록 한다.
스프링에 Converter 적용
@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());
}
}
- 스프링에서 ConversionService를 제공하기 때문에 WebMvcConfigurer가 제공하는 addFormatters()로 컨버터를 등록해서 사용할 수 있다.
@GetMapping("/converter-view")
public String converterView(Model model) {
model.addAttribute("number", 10000);
model.addAttribute("ipPort", new IpPort("127.0.0.1", 8080));
return "converter-view";
}
//HTML
<li>${number}: <span th:text="${number}"></span></li>
<li>${{number}}: <span th:text="${{number}}"></span></li>
<li>${ipPort}: <span th:text="${ipPort}"></span></li>
<li>${{ipPort}}: <span th:text="${{ipPort}}"></span></li>
- 뷰 템플릿(타임리프)에서 컨버터를 적용하기 위해서는 ${{...}} 형태를 사용해야 한다.
- 뷰 템플릿에서 내용을 출력하기 위해서 문자열로 변환해서 출력해야하고 ${{...}} 의 경우에는 String으로 변환해주는 컨버터가 작동한다.
- 기본 타입의 경우는 컨버터를 사용하지 않아도 문자로 자동으로 변환해주지만 객체의 경우에는 컨버터를 사용하지 않으면 toString()으로 출력된다.
- 타임리프의 th:field도 컨버전 서비스를 자동으로 적용해준다.
- 컨버전 서비스를 사용하지 않으려면 th:value를 사용하면 된다.
- 타임리프 표현식
- 변수 표현식 : ${...}
- 컨버전 서비스 적용 : ${{...}}
포멧터 - Formatter
- 포멧터 : 객체를 특정한 포멧에 맞춰 문자로 출력하거나 반대의 역할을 하는 것에 특화된 기능
- 컨버터의 특별한 버전
- 날짜 객체를 연-월-일 시:분:초와 같이 출력하는 경우, 숫자를 1,000과 같이 ,를 넣어서 문자로 표현하는 경우 등
- Converter vs Formatter
- Converter : 범용 (객체 -> 객체)
- Formatter : 문자에 특화 (객체 -> 문자, 문자 -> 객체) + 현지화 (Locale) 적용
- Formatter 인터페이스
- String print(T object, Locale locale) : 객체를 문자로 변경한다.
- T parse(String text, Locale locale) : 문자를 객체로 변경한다.
@Slf4j
public class MyNumberFormatter implements Formatter<Number> {
@Override
public Number parse(String text, Locale locale) throws ParseException {
log.info("text={}, locale={}", text, locale);
//"1,000" -> 1000
return NumberFormat.getInstance(locale).parse(text);
}
@Override
public String print(Number object, Locale locale) {
log.info("object={}, locale={}", object, locale);
return NumberFormat.getInstance(locale).format(object);
}
}
- "1,000"처럼 숫자 중간의 쉼표를 적용하려면 자바에서 기본으로 제공하는 NumberFormat 객체를 사용하면 된다.
- Locale 정보를 통해 나라별로 다른 숫자 포멧을 만들어준다.
- 스프링에서 적용 시 Formatter도 마찬가지로 WebMvcConfigurer에 등록해서 사용한다.
스프링이 제공하는 기본 포멧터
- 스프링에서 어노테이션 기반으로 원하는 형식을 지정해서 사용할 수 있는 유용한 포멧터를 제공한다
- @NumberFormat : 숫자 형식 지정 포멧터 사용
- @DateTimeFormat : 날짜 관련 형식 지정 포멧터 사용
class Form {
@NumberFormat(pattern = "###,###")
private Integer number;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime;
}
- parttern 형식에 맞게 포멧터가 적용된다.
- 반대의 경우에도 마찬가지로 적용된다.
- 메시지 컨버터(HttpMessageConverter)는 컨버전 서비스가 적용되지 않는다.
- 객체 -> JSON으로 변환하는 경우 Jackson같은 라이브러리를 사용하기에 해당 라이브러리가 제공하는 설정을 통해 포멧을 지정해야한다.
정리
- 컨버터든 포멧터든 등록 방법은 다르지만 사용할 때 컨버전 서비스를 통해서 일관되게 사용할 수 있다.
- 스프링에서 ConversionService를 제공하기 때문에 WebMvcConfigurer가 제공하는 addFormatters()로 컨버터, 포멧터를 등록해서 사용할 수 있다.
- 컨버전 서비스는 @RequestParam, @ModelAttribute, @PathVariable, 뷰 템플릿에서 사용할 수 있다.
- Converter vs Formatter
- Converter : 범용 (객체 -> 객체)
- Formatter : 문자에 특화 (객체 -> 문자, 문자 -> 객체) + 현지화 (Locale) 적용
- 포멧터와 컨버터 모두 기본 타입을 변환하는 것은 자동으로 지원하기에 객체를 변환하는 것 위주로 등록해서 사용
출처 : [인프런 김영한 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술]
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 강의 | 김영한 - 인프런
김영한 | 웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습
www.inflearn.com
'Spring > [인프런 김영한 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술]' 카테고리의 다른 글
[인프런 김영한 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술] 파일 업로드 (5) | 2024.11.11 |
---|---|
[인프런 김영한 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술] API 예외 처리 (2) | 2024.11.06 |
[인프런 김영한 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술] 예외 처리와 오류 페이지 (2) | 2024.10.18 |
[인프런 김영한 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술] 로그인 처리 - 필터, 인터셉터 (6) | 2024.10.16 |
[인프런 김영한 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술] 로그인 처리 - 쿠키, 세션 (4) | 2024.10.15 |