Spring/Spring

[Spring][Field] 2. Formatting

noahkim_ 2025. 4. 6. 21:31

1. Formatter

  • 클라이언트 환경에서 문자열 ↔️ 객체 간 변환을 처리하는 인터페이스

 

특징

특징 설명
강타입 기반 Formatter<T> 제네릭 타입으로 타입 안정성 보장
Locale 지원
Locale 정보를 기반으로 사용자 친화적인 지역화 포맷 처리 가능
양방향 변환 지원 print(): 객체 → 문자열
parse(): 문자열 → 객체
예외 처리
변환 실패 시 ParseException 또는 IllegalArgumentException 발생 가능
Thread-unsafe
상태(state)를 가지는 경우 다중 스레드 환경에서 안전하지 않음 (주의 필요)

 

 

예시) DateFormatter

더보기
public final class DateFormatter implements Formatter<Date> {
    private String pattern;

    public String print(Date date, Locale locale) { ... }

    public Date parse(String formatted, Locale locale) throws ParseException { ... }
}
public class MyDateFormatter implements Formatter<LocalDate> {
    @Override
    public LocalDate parse(String text, Locale locale) throws ParseException {
        if (locale.equals(Locale.KOREA)) {
            return LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyy.MM.dd"));
        } else {
            return LocalDate.parse(text, DateTimeFormatter.ofPattern("MM/dd/yyyy"));
        }
    }

    @Override
    public String print(LocalDate object, Locale locale) {
        if (locale.equals(Locale.KOREA)) {
            return object.format(DateTimeFormatter.ofPattern("yyyy.MM.dd"));
        } else {
            return object.format(DateTimeFormatter.ofPattern("MM/dd/yyyy"));
        }
    }
}

 

AnnotationFormatterFactory

  • 특정 어노테이션이 붙은 필드에 Formatter 적용

 

예시) @NumberFormat 연결

더보기
public final class NumberFormatAnnotationFormatterFactory
        implements AnnotationFormatterFactory<NumberFormat> {
    public Set<Class<?>> getFieldTypes() { ... }
    public Printer<Number> getPrinter(NumberFormat annotation, Class<?> fieldType) { ... }
    public Parser<Number> getParser(NumberFormat annotation, Class<?> fieldType) { ... }
}
public class MyModel {
    @NumberFormat(style = Style.CURRENCY)
    private BigDecimal decimal;
    
    @DateTimeFormat(iso = ISO.DATE)
    private Date date;
}

 

Annotation

어노테이션 대상 타입 기능 설명
지원 포맷
@DateTimeFormat Date, Calendar, LocalDate, LocalDateTime 날짜 및 시간 문자열 ↔ 날짜 객체 iso, pattern
@NumberFormat Number, BigDecimal, Integer 등 숫자 ↔ 문자열 style, pattern

 

 

예시) @DateTimeFormat

더보기
@GetMapping("/date")
public String getDate(@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date) {
    return "입력 받은 날짜: " + date;
}
@GetMapping("/custom-date")
public String getCustomDate(@RequestParam @DateTimeFormat(pattern = "yyyy/MM/dd") LocalDate date) {
    return "입력 받은 날짜: " + date;
}

 

예시) @NumberFormat

더보기
public class Product {
    @NumberFormat(pattern = "#,###.##")
    private BigDecimal price;

    // getter/setter 생략
}
@PostMapping("/product")
public String addProduct(@ModelAttribute Product product) {
    return "가격: " + product.getPrice();
}

 

  • 요청 예시: price=1,234.56
  • 자동 변환: "1,234.56" → BigDecimal.valueOf(1234.56)

 

 

3. FormatterRegistry

  • Formatter, Convereter, Printer, Parser, AnnotationFormatterFactory를 등록하기 위한 SPI
public interface FormatterRegistry extends ConverterRegistry {
    void addFormatter(Formatter<?> formatter);
    void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter);
    void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory);
}

 

특징

특징 설명
중앙 집중식 등록
한 곳에서 포맷터를 등록하여 애플리케이션 전반에 걸쳐 일관된 포맷 유지 가능
타입 기반 또는 애노테이션 기반 등록 지원
특정 타입 또는 특정 애노테이션에 따라 포맷터 등록 가능
(addFormatter, addFormatterForFieldAnnotation)
ConversionService와 연동 가능
FormattingConversionService로 통합되어 Converter, Formatter를 함께 사용 가능

 

4. FormatterRegistrar

  • FormatterRegistry에 관련된 포맷터들을 묶어서 등록하기 위한 인터페이스
public interface FormatterRegistrar {
    void registerFormatters(FormatterRegistry registry);
}

 

용도

용도 항목 설명
카테고리별 포맷터 일괄 등록
날짜, 숫자 등 특정 도메인의 관련 포맷터들을 한 번에 등록할 수 있음
선언적 등록의 한계 보완
애노테이션 기반 또는 개별 등록으로는 처리하기 어려운 경우, 프로그래밍 방식으로 유연하게 등록 가능
<T> 타입 ≠ 필드 타입인 경우
포맷터의 제네릭 타입 <T>과 실제 적용할 필드 타입이 다를 경우, 명시적으로 매핑하여 등록 가능

 

 

5. Global Date/Time Format 설정

  • 기본적으로 DateFormat.SHORT 스타일로 문자열을 날짜 타입으로 변환. (예: 24. 4. 6. → Date)
  • 기본 셋팅 바꾸려면 설정 필요

 

기본 셋팅 바꾸는 법

@Configuration
public class ApplicationConfiguration {

    @Bean
    public FormattingConversionService conversionService() {
        // 1. 기본 포맷터 등록 X
        DefaultFormattingConversionService conversionService =
                new DefaultFormattingConversionService(false);

        // 2. @NumberFormat 사용 가능하도록 수동 등록
        conversionService.addFormatterForFieldAnnotation(
                new NumberFormatAnnotationFormatterFactory());

        // 3. LocalDate, LocalDateTime 등에 yyyyMMdd 포맷 적용
        DateTimeFormatterRegistrar dateTimeRegistrar = new DateTimeFormatterRegistrar();
        dateTimeRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"));
        dateTimeRegistrar.registerFormatters(conversionService);

        // 4. java.util.Date 등에 yyyyMMdd 포맷 적용
        DateFormatterRegistrar dateRegistrar = new DateFormatterRegistrar();
        dateRegistrar.setFormatter(new DateFormatter("yyyyMMdd"));
        dateRegistrar.registerFormatters(conversionService);

        return conversionService;
    }
}

 

  1. 기본 포맷터 등록 하지 말기
  2. @NumbeFormat 수동으로 다시 등록
  3. java.time용 전역 날짜 포맷 등록 (LocalDate, LocalDateTime)
  4. java.util용 전역 날짜 포맷 등록 (Date, Calendar)

 

 

 


출처