Formatter
📌 주로 사용자 지정 포맷을 적용해 데이터 변환을 처리할 때 사용된다. Formatter는 ConversionService와 비슷한 목적을 가지지만 문자열을 객체로 변환하거나 객체를 문자열로 변환하는 과정에서 포맷팅을 세밀하게 제어할 수 있다.
- 객체를 특정한 포맷에 맞춰서 문자로 출력하는 기능에 특화된것이 Fomatter이다.
- Converter보다 조금 더 세부적인 기능이라고 생각하면 된다.
Spring에서 Formatter와 Converter는 모두 데이터 변환과 관련이 있지만, 주요 사용 목적과 기능에서 차이가 있습니다. 이 둘은 서로 보완적으로 사용되며, 특정 상황에서 더 적합한 도구를 선택할 수 있습니다.
Formatter와 Converter의 차이
특징 Formatter Converter주요 목적 | 데이터 변환과 포맷팅(formatting) 지원. | 단순히 타입 변환에 집중. |
양방향 변환 | String ↔ Object 형태로 양방향 변환 지원. | 한 방향으로만(Source → Target) 변환 가능. |
대상 데이터 | 주로 사용자 입력 데이터를 처리하거나 포맷팅이 필요한 데이터. | 데이터 타입 간 변환(예: String → Integer, String → Enum). |
인터페이스 구조 | Formatter<T> (포맷 변환 및 역변환 메서드 포함). | Converter<S, T> (단일 변환 메서드만 포함). |
사용 위치 | 사용자 입력 데이터(폼 데이터, 요청 파라미터 등) 처리. | 내부적으로 데이터 바인딩 및 단순 변환 처리. |
Spring 사용 방식 | FormattingConversionService를 통해 등록 및 관리. | ConversionService를 통해 등록 및 관리. |
Formatter
Formatter는 주로 데이터를 특정 형식(포맷)으로 변환하거나, 문자열 데이터를 객체로 역변환하는 데 사용됩니다.
인터페이스 정의
public interface Formatter<T> {
String print(T object, Locale locale); // 객체 → 문자열 (포맷팅)
T parse(String text, Locale locale) throws ParseException; // 문자열 → 객체 (역변환)
}
Formatter 사용 예: String ↔ LocalDate
public class LocalDateFormatter implements Formatter<LocalDate> {
@Override
public LocalDate parse(String text, Locale locale) throws ParseException {
return LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
@Override
public String print(LocalDate object, Locale locale) {
return object.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
}
Formatter 등록
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new LocalDateFormatter());
}
}
사용 예
컨트롤러 메서드에서 LocalDate를 자동 변환:
@GetMapping("/date")
public String getDate(@RequestParam LocalDate date) {
return "Formatted Date: " + date.toString();
}
요청:
GET /date?date=2023-12-25
Converter
Converter는 단순히 하나의 데이터 타입을 다른 데이터 타입으로 변환합니다.
인터페이스 정의
public interface Converter<S, T> {
T convert(S source); // 소스 타입 → 대상 타입
}
Converter 사용 예: String → Integer
public class StringToIntegerConverter implements Converter<String, Integer> {
@Override
public Integer convert(String source) {
return Integer.valueOf(source);
}
}
Converter 등록
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToIntegerConverter());
}
}
사용 예
컨트롤러 메서드에서 Integer를 자동 변환:
@GetMapping("/number")
public String getNumber(@RequestParam Integer number) {
return "Number: " + number;
}
요청:
GET /number?number=123
Formatter와 Converter의 사용 사례 비교
기능 Formatter Converter날짜 변환 | 2023-12-25 ↔ LocalDate | 지원하지 않음 (포맷팅 기능 없음). |
Enum 변환 | ACTIVE ↔ Status.ACTIVE | String → Status.ACTIVE. |
사용자 입력 처리 | 사용자 입력 데이터를 읽어 포맷팅. | 단순 데이터 변환 작업. |
예제 요청 파라미터 | GET /date?date=2023-12-25 | GET /number?number=123. |
Converter와 Formatter의 동작 구조
- Converter:
- 입력 데이터가 단순히 변환되기만 하면 되는 경우 사용.
- 예: 쿼리 파라미터 123 → Integer.
- Formatter:
- 사용자 입력 데이터가 특정 형식에 따라 변환되고 역변환이 필요한 경우 사용.
- 예: yyyy-MM-dd → LocalDate.
결론
- Converter: 단순히 데이터 타입을 변환할 때 사용.
- 비유: 변환기. 한 단위를 다른 단위로 바꾸는 도구.
- Formatter: 데이터 변환과 동시에 포맷팅이 필요한 경우 사용.
- 비유: 포맷터. 데이터를 사람이 읽기 좋은 형식으로 변환하는 도구.
선택 기준:
- 단순 타입 변환: Converter 사용.
- 포맷팅과 역변환 모두 필요: Formatter 사용.
- Locale
- 지역 및 언어 정보를 나타내는 객체.
- 언어코드 en, ko
- 국가코드 US, KR
- 특정 지역 및 언어에 대한 정보를 제공하여 국제화 및 지역화 기능을 지원한다.
- 국제화
- Locale 정보에 따라서 한글을 보여줄지 영문을 보여줄지 선택할 수 있다.
- 지역 및 언어 정보를 나타내는 객체.
Formatter Interface
- Printer, Parser 상속
- 객체를 문자로 변환하고 문자를 객체로 변환하는 두가지 기능을 모두 가지고 있다.
- Printer
Object를 String으로 변환한다.
Parser
String을 Object로 변환한다.
Formatter 적용
@Slf4j
public class PriceFormatter implements Formatter<Number> {
@Override
public Number parse(String text, Locale locale) throws ParseException {
log.info("text = {}, locale={}", text, locale);
// 변환 로직
// NumberFormat이 제공하는 기능
NumberFormat format = NumberFormat.getInstance(locale);
// "10,000" -> 10000L
return format.parse(text);
}
@Override
public String print(Number object, Locale locale) {
log.info("object = {}, locale = {}", object, locale);
// 10000L -> "10,000"
return NumberFormat.getInstance(locale).format(object);
}
}
Number
- Integer, Long, Double 등의 부모 클래스
class PriceFormatterTest {
PriceFormatter formatter = new PriceFormatter();
@Test
void parse() throws ParseException {
// given, when
Number result = formatter.parse("1,000", Locale.KOREA);
// then
// parse 결과는 Long
Assertions.assertThat(result).isEqualTo(1000L);
}
@Test
void print() {
// given, when
String result = formatter.print(1000, Locale.KOREA);
// then
Assertions.assertThat(result).isEqualTo("1,000");
}
}
Spring Formatter
📌 Spring의 Formatter는 문자열 데이터를 특정 객체로 변환하거나, 객체를 특정 문자열 형식으로 변환(포맷팅)하는 데 사용되는 인터페이스입니다. 데이터 포맷팅과 역변환을 쉽게 처리할 수 있도록 Spring에서 제공됩니다
FormattingConversionService
📌 ConversionService와 Formatter를 결합한 구현체로 타입 변환과 포맷팅이 필요한 모든 작업을 한 곳에서 수행할 수 있도록 설계되어 있어서 다양한 타입의 변환과 포맷팅을 쉽게 적용할 수 있다.
- 어댑터 패턴을 사용하여 Formatter가 Converter처럼 동작하도록 만들어준다.
DefaultFormattingConversionService
📌 FormattingConversionService + 통화, 숫자관련 Formatter를 추가한것
public class FormattingConversionServiceTest {
@Test
void formattingConversionService() {
// given
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
// Converter 등록
conversionService.addConverter(new StringToPersonConverter());
conversionService.addConverter(new PersonToStringConverter());
// Formatter 등록
conversionService.addFormatter(new PriceFormatter());
// when
String result = conversionService.convert(10000, String.class);
// then
Assertions.assertThat(result).isEqualTo("10,000");
}
}
- ConversionService가 제공하는 convert()를 사용하면 된다.
SpringBoot의 기능
📌 SpringBoot는 기본적으로 WebConversionService를 사용한다.
- DefaultFormattingConversionService 상속
Spring이 제공하는 Formatter
📌 Spring은 어노테이션 기반으로 원하는 형식의 Formatter를 사용할 수 있도록 기능을 제공한다.
- Annotation
- DTO 필드들에 적용 가능
- @NumberFormat
- 숫자 관련 지정 Formatter 사용
- NumberFormatAnnotationFormatterFactory
- @DateTimeFormat
- 날짜 관련 지정 Formatter 사용
- Jsr310DateTimeFormatAnnotationFormatterFactory
'Back-End (Web) > Spring' 카테고리의 다른 글
fetch join (1) | 2025.01.21 |
---|---|
API 예외처리 (0) | 2025.01.19 |
TypeConverter (0) | 2025.01.10 |
HttpMessageConverter (0) | 2025.01.09 |
ArgumentResolver (0) | 2025.01.08 |