public class Whiteship {

    String name;

    public Whiteship(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class SpringSprout {

    private Whiteship whiteship;

    private int num;

    public Whiteship getWhiteship() {
        return whiteship;
    }

    public void setWhiteship(Whiteship whiteship) {
        this.whiteship = whiteship;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
}

이렇게 두 개의 클래스가 있을 때 Whiteship을 SpringSprout로 주입하는 빈 설정을 다음과 같이 했다.

    <bean class="sandbox.convert.SpringSprout">
        <property name="whiteship" value="keesun"/>
        <property name="num" value="1"/>
    </bean>

동작할까 안할까?

당연히 안한다. Whiteship 타입이 필요한데 keesun이라는 문자열을 던져주다니 장난 하는게냐 라고 예외를 던질꺼다.

하지만 되게 만들 수 있다. 이전까지는 PropertyEditorSupport 클래스를 확장하여 아주 손쉽게 구현할 수 있었다.

public class WhiteshipPE extends PropertyEditorSupport {

    @Override
    public String getAsText() {
        return ((Whiteship)getValue()).getName();
    }

    @Override
    public void setAsText(String name) throws IllegalArgumentException {
        setValue(new Whiteship(name));
    }
}

이런 코드는 특히 MVC에서 바인딩 할때 매우 유용하다. 이걸 안쓰면 request에서 일일히 꺼내서 노가다를 해야하는데 난 절대로 그러고 싶지 않다.

그런데 PE의 문제는 쓰레드-세이프하지 않다는 것이다. 그래서 PE를 등록할 때 조심해야 한다. 특히 전역 변수를 가지는 PE를 싱글톤으로 바인더에 등록해버리지 않았나 조심해야 한다. 반드시 그때 그때 new를 사용해서 등록해 주도록 하고 PE 생성자에 필요한 레퍼런스를 전달해주는게 안전하다. 아마 이 내용도 사부님 책에 들어갈 것 같으니 자세한 내용은 그 책을 참고하도록 하자.

PE의 대안이자 좀 더 범용적인 변환기 역할로 3.0에 도입된 것이 ConversionService이고 ConversionServiceFactoryBean에 Converter, ConveterFacrtory, Formatter 등을 등록해놓고 변환을 부탁할 수 있게 되었다.

public class WhiteshipConverter implements Converter<String, Whiteship> {
    public Whiteship convert(String source) {
        return new Whiteship(source);
    }
}

그런데;; PE와 역할이 중복되는데;;; 둘 다 빈으로 등록해둘수 있다.

    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="propertyEditorRegistrars">
            <bean class="sandbox.convert.CustomPERegister">
            </bean>
        </property>
    </bean>

    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="sandbox.convert.WhiteshipConverter"/>
            </set>
        </property>
    </bean>

결국 문제는 대체 String을 Whiteship으로 바꿔야 할 때 PE를 쓸 것이냐 CS를 쓸 것이냐이다. 무엇을 쓰게 될지... 그게 궁금했었다. 그런데 마침 오늘 사부님이 퀴즈를 냈고;; 난 스케줄을 팽개치고 매달렸다;;

1차 문제 상황

CS와 PE가 등록되어 있을 때 PE가 이겼다.
하지만 난 CS가 이기는 경우를 예제에서 본적이 있었다. 그래서 도무지 납득이 되지 않았고 어떻게 설정해야 가능한지 궁금했다.

CS만 등록했을 땐 CS로 동작했다.
PE만 등록했을 땐 PE로 동작했다.
둘다 등록했을 땐 PE가 동작헀다.

2차 문제 상황

Whiteship만 가지고 테스트를 했었는데 기본 타입도 추가해봤다. int num이 그것이다. 이제 더 큰 문제가 생겼다.
Whiteship만 PE가 이기고 나머지 기본 타입은 CS가 이겼다.
정말 깜놀이었다. 이사실을 발견하지 않았다면 난 그냥 클래스로더를 공부했을 것이다.

어디선가 기본 PE를 덮어쓰거나 없애버린 거 아닌지 궁금했고 소스 코드를 뒤져보기 시작했다.
시간이 손살같이 지난간다.
우울해지기 시작한다.

3차 결론

소스 코드를 뒤적거리다가 ConvertingPropertyEditorAdapter 클래스를 찾았다.
이 클래스는 ConversionService를 사용해서 PE를 노출시켜주는 어댑터다.
겉으로는 PE를 등록한것 같지만 실제로는 ConvesionService를 사용하는 것이다.ㅋ

스프링에서 내부에서 저걸 사용해서 기본 PE들을 등록하도록 바꿨는지는 확인하지 않았다.
왠지 답이 아닌것 같다. OTL..
내가 졌다.

트릭같은 기법을 써서라도 PE를 누르고 아니 속이고 CS가 동작하게 했으니...그만 놓아줄 수 있을 것 같다.
어서 사부님 책이 나와주길....

ps: 위에 나온 코드는 급조한 코드오니 조심할 부분을 건너뛴 곳도 있습니다. 주의하세요.