✅ Formatter
♐ Formatter 이란?
- 주로 사용자 지정 포맷을 적용해 데이터 변환을 처리할 때 사용된다.
- Formatter는 ConversionService와 비슷한 목적을 가지지만 문자열을 객체로 변환하거나 객체를 문자열로 변환하는 과정에서 포맷팅을 세밀하게 제어할 수 있다.
공식문서
https://docs.spring.io/spring-framework/reference/core/validation/format.html
Spring Field Formatting :: Spring Framework
As discussed in the previous section, core.convert is a general-purpose type conversion system. It provides a unified ConversionService API as well as a strongly typed Converter SPI for implementing conversion logic from one type to another. A Spring conta
docs.spring.io
♐ Formatter Interface
- Printer, Parser 상속
- 객체를 문자로 변환하고 문자를 객체로 변환하는 두가지 기능을 모두 가지고 있다.
1️⃣ Printer
- Object를 String으로 변환한다.
2️⃣ Parser
- String을 Object로 변환한다.
✅ 실습
- 숫자(10000)를 금액 형태(10,000)로 변환하는 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);
}
}
테스트 코드
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
♐FormattingConversionService
- ConversionService와 Formatter를 결합한 구현체
- 타입 변환과 포맷팅이 필요한 모든 작업을 한 곳에서 수행할 수 있도록 설계되어 있어서 다양한 타입의 변환과 포맷팅을 쉽게 적용할 수 있다.
♐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");
}
}
✅ Spring에 활용되는 Format Annotation
♐@ NumberFormat ,@DateFormat
- 숫자 관련 지정 Formatter 사용
- 날짜 관련 지정 Formatter 사용
@Data
public class FormatForm {
@NumberFormat(pattern = "#,###.##")
private BigDecimal price;
@DateTimeFormat(pattern = "dd-MM-yyyy")
private LocalDate orderDate;
}
@RestController
public class FormatController {
@PostMapping("/format")
public ResponseEntity<String> format(@ModelAttribute FormatForm form) {
return new ResponseEntity<>(
"form.getPrice() = " + form.getPrice() +
" form.getOrderDate() = " + form.getOrderDate(),
HttpStatus.OK
);
}
}
결과
♐ @JsonFormat
- `@JsonFormat`은 날짜 형식이나 숫자 포맷을 지정할 수 있다.
- 콤마를 포함한 숫자는 Jackson이 자동으로 변환하지 않으므로 커스텀 처리가 필요하다.
- 커스텀 데이터의 변환이 필요한 경우 `Deserialize` 사용
@Data
public class JsonFormatDtoV2 {
@JsonDeserialize(using = CurrencyDeserializer.class)
private BigDecimal price;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
private LocalDate orderDate;
}
@PostMapping("/json-format/annotation")
public ResponseEntity<String> jsonFormatAnnotation(@RequestBody JsonFormatDtoV2 dto) {
return new ResponseEntity<>(
"dto.getPrice() = " + dto.getPrice() +
" dto.getOrderDate() = " + dto.getOrderDate(),
HttpStatus.OK
);
}
공식문서
Spring Field Formatting :: Spring Framework
As discussed in the previous section, core.convert is a general-purpose type conversion system. It provides a unified ConversionService API as well as a strongly typed Converter SPI for implementing conversion logic from one type to another. A Spring conta
docs.spring.io
'Spring' 카테고리의 다른 글
[Spring]오프셋 페이징보다 커서 페이징을 써야하는 이유 (1) | 2025.04.25 |
---|---|
[Spring/JPQL] JPQL이 무엇인지 알고 사용하자 (1) | 2025.04.18 |
[Spring]HttpMessageConverter (0) | 2025.04.16 |
[SPRING/JPA]양방향 관계시 사용하는 CASCADE에 대해 알아보자 (1) | 2025.04.11 |
[Spring/JPA] 영속성 컨텍스트 (0) | 2025.04.11 |