스프링/4. 스프링 MVC-2

80. 포맷터

sdafdq 2023. 9. 24. 00:57

컨버터는 타입에 제한이 없다.

 

근데 웹으로써 생각해봤을 때, 대부분의 컨버팅이 필요한 상황은 문자 <-> 객체 혹은 타입 이다.

거기에서 그칠 뿐 아니라, 1000 -> "1,000"이라던지, 아니면 날짜 포맷에 맞춘다던지.

 

 

 

그래서, 

포맷터 라는 게 있다.

이거는 객체 <-> 문자, 타입 <-> 문자 등 문자에 특화된 인터페이스이다.

게다가 Locale, 현지화와 연동하여 사용할 수도 있게끔 지원해 준다.

 

 

실제 포맷터 인터페이스는

public interface Formatter<T> extends Printer<T>, Parser<T> {

}

이렇게 생겼다.

Printer는 객체 -> 문자인 인터페이스이고, 

Parse는 문자 -> 객체인 인터페이스이다.

 

각자 들어가서 보면

@FunctionalInterface
public interface Printer<T> {
	String print(T object, Locale locale);
}

 

@FunctionalInterface
public interface Parser<T> {
	T parse(String text, Locale locale) throws ParseException;
}

Locale을 받는다.

 

 

여튼, 상속받아서 한번 만들어 봤다.

public class MyNumberFormatter implements Formatter<Number> {

    @Override
    public Number parse(String text, Locale locale) throws ParseException {
        String clearText = text.replaceAll(",","");
        return Integer.parseInt(clearText);
    }

    @Override
    public String print(Number object, Locale locale) {
        if((int)object < 999) return String.valueOf(object);
        StringBuffer intTOstr = new StringBuffer(String.valueOf(object));
        for(int i = intTOstr.length() - 3; i >= 0; i -= 3){
            intTOstr.insert(i,",");
        }
        return intTOstr.toString();
    }
}

Formatter<Number> 어차피 한쪽은 문자열이라 다른 한쪽의 대상 타입만 쓰면 된다.

Number는 Integer, Float등의 상위 이다.

 

대충 이렇게 하긴 했는데.. 물론 잘 된다. 근데 이건 한국 기준이다.

이렇게 구현해 보니까 괜히 로컬을 인자로 받는 게 아니었다.

 

public class FormatterTest {
    @Test
    public void intToString(){
        MyNumberFormatter formatter = new MyNumberFormatter();
        int num1 = 100;
        int num2 = 1000;
        String intToStr1 = formatter.print(num1, null);
        String intToStr2 = formatter.print(num2, null);
        assertThat(intToStr1).isEqualTo("100");
        assertThat(intToStr2).isEqualTo("1,000");
    }

    @Test
    public void StringToInt() throws ParseException {
        MyNumberFormatter formatter = new MyNumberFormatter();
        String str = "1,000,000";
        Number num = formatter.parse(str,null);
        assertThat(num).isEqualTo(1000000);
    }
}

내가 짠 테스트 코드는 이러 하고,

 

 

선생님의 로직에 따라 (이미 사실 이러 한 범용적인 것은 자바 및 스프링에서 제공해 준다.)

public class MyNumberFormatter implements Formatter<Number> {

    @Override
    public Number parse(String text, Locale locale) throws ParseException {
        NumberFormat format = NumberFormat.getInstance(locale);
        return format.parse(text);
    }

    @Override
    public String print(Number object, Locale locale) {
        NumberFormat format = NumberFormat.getInstance(locale);
        return format.format(object);
    }
}

NumberFormat.getInstance(locale) 해서 로컬에 맞는 정보로 setting된 NumberFormat의 객체를 가져오고,

NumberFormat.parse(문자)

문자 -> Number

(여기선 Long 타입으로 반환됨.)

 

NumberFormat.format(Number)

Number ->  문자

 

로컬 타입에 맞게 바뀐다.

테스트코드는 반환타입을 Long타입인 1000000L로 바꾸면 문제없다.