이전까지 학습한 Converter 는 입력과 출력 타입에 제한이 없는, 범용 타입 변환 기능을 제공합니다.
일반적인 웹 애플리케이션 환경을 생각해보면 불린 타입을 숫자로 바꾸는 것 같은 범용 기능 보다는 개발자 입장에서는
문자를 다른 타입으로 변환하거나, 다른 타입을 문자로 변환하는 상황이 대부분입니다.
앞서 살펴본 예제들을 떠올려 보면 문자를 다른 객체로 변환하거나 객체를 문자로 변환하는 일이 대부분입니다.
웹 애플리케이션에서 객체를 문자로, 문자를 객체로 변환하는 예 :
• 화면에 숫자를 출력해야 하는데, Integer String 출력 시점에 숫자 1000. 문자 "1,000" 이렇게 1000 단위에 쉼표를 넣어서 출력하거나, 또는 "1,000" 라는 문자를 1000 이라는 숫자로 변경해야 합니다.
• 날짜 객체를 문자인 "2021-01-01 10:50:11" 와 같이 출력하거나 또는 그 반대의 상황
Locale
여기에 추가로 날짜 숫자의 표현 방법은 Locale 현지화 정보가 사용될 수 있습니다.
이렇게 객체를 특정한 포멧에 맞추어 문자로 출력하거나 또는 그 반대의 역할을 하는 것에 특화된 기능이 바로 포맷터( Formatter )입니다. 포맷터는 컨버터의 특별한 버전으로 이해하면 됩니다.
Converter vs Formatter
• Converter 는 범용(객체 객체)
• Formatter 는 문자에 특화(객체 문자, 문자 객체) + 현지화(Locale)
• Converter 의 특별한 버전
포맷터 - Formatter 만들기
• 포맷터( Formatter )는 객체를 문자로 변경하고, 문자를 객체로 변경하는 두 가지 기능을 모두 수행합니다.
• String print(T object, Locale locale) : 객체를 문자로 변경.
• T parse(String text, Locale locale) : 문자를 객체로 변경.
Formatter인터페이스는 아래와 같습니다.
print 메소드를 가지고 있는 Printer와 parse 메소드를 가지고 있는 Paser를 상속받고 있습니다.
이제 "1,000" -> 1000, 1000->"1,000"으로 바꿔주는 Formatter를 구현해보겠습니다.
parse() 를 사용해서 문자를 숫자로 변환합니다. 참고로 Number 타입은 Integer , Long 과 같은 숫자 타입의 부모 클래스입니다.
print() 를 사용해서 객체를 문자로 변환합니다.
아래와 같이 테스트할 수 있습니다.
포맷터를 지원하는 컨버전 서비스
컨버전 서비스에는 컨버터만 등록할 수 있고, 포맷터를 등록할 수 는 없습니다. 그런데 생각해보면 포맷터는 객체 -> 문자 / 문자 -> 객체로 변환하는 특별한 컨버터일 뿐입니다.
포맷터를 지원하는 컨버전 서비스를 사용하면 컨버전 서비스에 포맷터를 추가할 수 있습니다.
내부에서 어댑터 패턴을 사용해서 Formatter 가 Converter 처럼 동작하도록 지원합니다.
FormattingConversionService 는 포맷터를 지원하는 컨버전 서비스입니다.
DefaultFormattingConversionService 는 FormattingConversionService에 기본적인 통화, 숫자 관련 몇가지 기본 포맷터를 추가해서 제공합니다.
위와 같이 컨버터, 포맷터를 등록하고, 컨버터를 사용하는 것처럼 포맷터를 사용하면 됩니다.
DefaultFormattingConversionService 상속 관계
FormattingConversionService 는 ConversionService 관련 기능을 상속받기 때문에 결과적으로 컨버터도 포맷터도 모두 등록할 수 있습니다. 그리고 사용할 때는 ConversionService 가 제공하는 convert 를 사용하면 됩니다.
추가로 스프링 부트는 DefaultFormattingConversionService 를 상속 받은 WebConversionService 를 내부에서 사용합니다.
이제 웹애플리케이션에 포맷터를 적용해보겠습니다.
먼저 WebConfig에 포맷터를 등록해야합니다.
18, 19라인의 StringToIntegerConverter와 IntegerToStringConverter를 주석처리했습니다.
왜냐하면 이들도 MyNumberFormatter처럼 문자와 숫자간의 변경을 지원하는 컨버터입니다. converter가 formatter보다 우선순위가 높기 때문에 StringToIntegerConverter와 IntegerToStringConverter가 등록되어있지 않아야 MyNumberFormatter가 작동합니다.
이제 우리가 이전에 만든 컨트롤러에 요청을 날려보겠습니다.
요청결과:
두번째 줄에 포맷터가 적용된 것을 볼 수 있습니다.
반대의 경우도 보겠습니다.
위의 컨트롤러에 아래와 같은 요청을 날리면
1,000,000을 1000000숫자로 바꿔줘야합니다.
잘 변경된 것을 볼 수 있습니다.
스프링이 제공하는 기본 포맷터
스프링은 자바에서 기본으로 제공하는 타입들에 대해 수 많은 포맷터를 기본으로 제공합니다.
IDE에서 Formatter 인터페이스의 구현 클래스를 찾아보면 수 많은 날짜나 시간 관련 포맷터가 제공되는 것을 확인할 수 있습니다.
그런데 포맷터는 기본 형식이 지정되어 있기 때문에, 객체의 각 필드마다 다른 형식으로 포맷을 지정하기는 어렵습니다.
스프링은 이런 문제를 해결하기 위해 애노테이션 기반으로 원하는 형식을 지정해서 사용할 수 있는 매우 유용한 포맷터 두 가지를 기본으로 제공합니다.
- @NumberFormat : 숫자 관련 형식 지정 포맷터 사용, NumberFormatAnnotationFormatterFactory
- @DateTimeFormat : 날짜 관련 형식 지정 포맷터 사용, Jsr310DateTimeFormatAnnotationFormatterFactory
아래와 같은 컨트롤러로 테스트해보겠습니다.
formatter-form
formatter-view
실행해보면 @NumberFormat, @DateTimeFormat 이 적용된 것을 볼 수 있습니다.
정리
컨버터를 사용하든, 포맷터를 사용하든 등록 방법은 다르지만, 사용할 때는 컨버전 서비스를 통해서 일관성 있게 사용할 수 있습니다.
주의!
메시지 컨버터( HttpMessageConverter )에는 컨버전 서비스가 적용되지 않습니다.
특히 객체를 JSON으로 변환할 때 메시지 컨버터를 사용하면서 이 부분을 많이 오해하는데, HttpMessageConverter 의 역할은 HTTP 메시지 바디의 내용을 객체로 변환하거나 객체를 HTTP 메시지 바디에 입력하는 것입니다. 예를 들어서 JSON을 객체로 변환하는 메시지 컨버터는 내부에서 Jackson 같은 라이브러리를 사용합니다. 객체를 JSON으로 변환한다면 그 결과는 이 라이브러리에 달린 것입니다. 따라서 JSON 결과로 만들어지는 숫자나 날짜 포맷을 변경하고 싶으면 해당 라이브러리가 제공하는 설정을 통해서 포맷을 지정해야 합니다. 결과적으로 이것은 컨버전 서비스와 전혀 관계가 없습니다.
컨버전 서비스는 @RequestParam , @ModelAttribute , @PathVariable , 뷰 템플릿 등에서 사용할 수 있습니다.
'Spring > spring mvc 심화' 카테고리의 다른 글
스프링 파일 업로드 (0) | 2021.07.20 |
---|---|
서블릿 파일 업로드 (0) | 2021.07.20 |
뷰 템플릿에 컨버터 적용하기 (0) | 2021.07.18 |
컨버젼 서비스 (0) | 2021.07.18 |
타입 컨버터 (0) | 2021.07.17 |