Formatter

최준호·2022년 6월 6일
0

Spring

목록 보기
33/48
post-thumbnail

지금까지 학습한 Converter는 입력과 출력 타입에 제한이 없는 범용 타입 변환 기능을 제공한다. 하지만 개발자는 문자를 다른 타입으로 변환하거나, 다른 타입을 문자로 변환하는 상황이 대부분이다.

ex) 1000이라는 int를 1,000으로 변경해주거나 날짜를 format에 맞게 변경하거나

Converter(객체 -> 객체)로 변경이 가능하고 Formatter는 문자에 특화되어 있는 기능이라고 생각하면 된다.

✅Formatter 만들기

@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
        NumberFormat format = NumberFormat.getInstance(locale);
        return format.parse(text);
    }

    @Override
    public String print(Number object, Locale locale) {
        log.info("object = {}, locale = {}", object, locale);
        // 1000 -> 1,000
        NumberFormat format = NumberFormat.getInstance(locale);
        String numberString = format.format(object);
        return numberString;
    }
}

SpringBoot의 Formatter를 상속받아서 parse와 print를 상속받아서 작성한다.

class MyNumberFormatterTest {

    MyNumberFormatter formatter = new MyNumberFormatter();

    @Test
    void parse() throws ParseException {
        Number result = formatter.parse("1,000", Locale.KOREA);
        assertEquals(result, 1000L);
    }

    @Test
    void print() {
        String result = formatter.print(1000, Locale.KOREA);
        assertEquals(result, "1,000");
    }
}

Test 코드를 작성해서 실행해보면

우리가 예상하는 결과로 잘 나오는 것도 확인할 수 있다. 이제 Formatter를 ConversionService에 등록해서 사용해보자!

✅FormattingConversionService

public class FormattingConversionServiceTest {

    @Test
    void formattingConversionService(){
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();

        //컨버터 등록
        conversionService.addConverter(new IpPortToStringConverter());
        conversionService.addConverter(new StringToIpPortConverter());
        //포맷터 등록
        conversionService.addFormatter(new MyNumberFormatter());

        //컨버터 사용
        IpPort ipPort = conversionService.convert("127.0.0.1:8080", IpPort.class);
        assertEquals(ipPort, new IpPort("127.0.0.1",8080));

        //포맷터 사용
        String convert = conversionService.convert(1000, String.class);
        assertEquals(convert, "1,000");
    }
}

다음과 같이 DefaultFormattingConversionService를 사용하면 converter와 formatter 모두 등록해서 사용할 수 있는 것을 확인할 수 있다. 그 이유는 DefaultFormattingConversionServiceConversionService를 상속받아서 사용하고 있기 때문에 그대로 사용할 수 있는 것이다.

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        //우선 순위 때문에 주석처리 formatter < converter 우선순위를 가지기 때문
//        registry.addConverter(new StringToIntegerConverter());
//        registry.addConverter(new IntegerToStringConverter());
        registry.addConverter(new IpPortToStringConverter());
        registry.addConverter(new StringToIpPortConverter());

        registry.addFormatter(new MyNumberFormatter());
    }
}

실제 프로젝트에 적용하기 위해서는 다음과 같이 적용하면 된다.

그럼 다음과 같이 formatter가 적용된 부분에는 ,가 들어가서 찍혀있는 것을 확인할 수 있다.

✅Spring에서 제공하는 기본 Formatter

Formatter Interface를 구현하고 있는 구현체들을 보면 다음과 같이 Spring에서 제공하는 수많은 Formatter를 볼수 있다.

@Controller
public class FormatterController {
    @GetMapping("/formatter/edit")
    public String formatterForm(Model model){
        Form form = new Form(10000, LocalDateTime.now());
        model.addAttribute("form", form);
        return "formatter-form";
    }

    @PostMapping("/formatter/edit")
    public String formatterEdit(Form form){
        return "formatter-view";
    }

    @Data
    @AllArgsConstructor
    static class Form{
        @NumberFormat(pattern = "###,###")
        private Integer number;
        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private LocalDateTime localDateTime;
    }
}

spring에서 제공하는 @NumberFormat@DateTimeFormat를 사용하면 숫자와 날짜 데이터는 간단하게 패턴을 적용해서 사용할 수 있다.

🍎주의점!

ConversionService@RequestParam, @ModelAttribute, @PathVariable, ViewTemplate에서 사용할 수 있으며 HttpMessageConverter에는 적용되지 않는다. HttpMessageConverter는 우리가 api로 통신할 때 사용되는 ResponseBody나 RequestBody에 사용되는 부분이다.

profile
해당 주소로 이전하였습니다. 감사합니다. https://ililil9482.tistory.com

0개의 댓글