아마 이번이 시리즈의 마지막이 아닐까 싶습니다. 지난번까지 DB에 저장할 값으로, Integer, String, Character를 지원하는 enum을 만들었고, 이번에는 enum의 목록을 특정 기준으로 정렬할 수 있는 기능을 추가했습니다.

enum 목록을 가져올 때, enum의 valus() 메서드를 사용하면, enum에 등록되어 있는 순서대로 상수들을 가져옵니다. 그러나 새로운 enum을 추가하고 변경하다보면 그 목록 순서도 변경되겠죠. 사용자가 특정 순서대로 목록이 보여지길 원한다면, 상수를 등록한 순서를 조정하는것이 아니라, 특정한 필드 기준으로 정렬을 해야겠다는 판단을 내렸습니다. 결국 새로운 필드를 하나 추가했습니다.

public enum UserCate implements PersistentEnum {
   
    ADMIN("admin", "관리자", 3), STAFF("staff", "직원", 1), SUPP("supp", "협력업체", 2);

...

    private UserCate(String value, String descr, int order) {
        this.value = value;
        this.descr = descr;
        this.order = order;
    }

...

    public int getOrder(){
        return order;
    }

}

이렇게 order라는 int 타입 변수를 하나 추가하고, 이 값으로 순서를 비교하는 Comparator를 만들었습니다. PersistentEnumUtils라는 클래스에 sortedListOf(Class<E> enumClass)를 추가로 만들어 줬습니다.

이제 정렬된 enum 목록이 필요할 떄는 다음과 같이 한 줄이면 됩니다.

    public List<CodeCate> getCodeCate(){
        return PersistentEnumUtil.sortedListOf(CodeCate.class);
    }

    public List<UserCate> getUserCate() {
        return PersistentEnumUtil.sortedListOf(UserCate.class);
    }

    public List<FamillyCate> getFamillyCate(){
        return PersistentEnumUtil.sortedListOf(FamillyCate.class);
    }

이런식으로 말이죠.

스프링, 하이버에서 enum 쓰기... 편하게 하기 위해 만들어 낸 결과물 구조는 다음과 같습니다.

- PersistentEnum 인터페이스
- PersistentEnumUtils 클래스
- PersistentEnumComparator 클래스
- GenericEnumPropertyEditor 클래스

PersistentEnum 인터페이스

    String getDescr();
   
    Object getValue();
   
    int getOrder();

이 셋은 스프링, 하이버네이트에서 enum을 사용할 때 거의 대부분 필요로 하게 될 것이며, GenericEnumPropertyEditor와 PersistentEnumUtils에서 사용할 인터페이스가 필요하기 때문에 만들었습니다.

PersistentEnumUtils 클래스

public static <E extends PersistentEnum> E valueOf(Class<E> enumType, Object value)

public static <E extends PersistentEnum> List sortedListOf(Class<E> enumType)

enum 클래스와 value로 enum을 찾아주는 메서드와 PersistentEnumComparator로 정렬한 list를 넘겨주는 메서드가 있습니다.

PersistentEnumComparator 클래스

    @Override
    public int compare(E e1, E e2) {
        return e1.getOrder() - e2.getOrder();
    }

이게 전부입니다.

GenericEnumPropertyEditor 클래스

PersistentEnum 인터페이스와 enum 클래스를 받아서 생성자, getAsText, setAsText를 재정의했습니다.

이렇게 네 개의 파일만 추가하면... enum을 다음과 같이 구현할 수 있습니다.

public enum UserCate implements PersistentEnum {
   
    ADMIN("admin", "관리자", 3), STAFF("staff", "직원", 1), SUPP("supp", "협력업체", 2);
   
    private final String value;
    private final String descr;
    private final int order;
   
    private UserCate(String value, String descr, int order) {
        this.value = value;
        this.descr = descr;
        this.order = order;
    }

    public String getValue() {
        return value;
    }
    public String getDescr() {
        return descr;
    }
    public int getOrder(){
        return order;
    }

}

뭐 특별한 건 없습니다. 그냥 enum에 인터페이스 하나 추가했을 뿐입니다.

하이버네이트를 사용할 땐, UserType 클래스가 필요하니까 UserType을 다음과 같이 만들어 줍니다.

public class UserCateType extends GenericEnumUserType<UserCate>{
   
}

이게 전부입니다. 클래스도 만들고 싶지 않지만, 하이버네이트 설정에 클래스 풀 패키지 경로를 줘야 하기 때문에 클래스를 안 만들 수가 없습니다.

    @Type(type="koma.domain.usertype.UserCateType")
    UserCate userCate;

자 이렇게 하면 하이버네이트 UserType 설정이 끝났습니다. 아무런 추가 구현도 없이 클래스 하나 만들었을 뿐인데 말이죠. DB 컬럼 타입과 그 안에 들어가는 값은 모두 getValue() 메서드의 리턴타입과 값에 맞게 정해집니다.

스프링 MVC에서 사용할 땐 PropertyEditor가 필요할 텐데.. 이건 전에도 말했듯이 클래스도 만들 필요없이 binder 설정하는 부분에 한 줄만 추가하면 됩니다.

binder.registerCustomEditor(UserCate.class, new GenericEnumPropertyEditor<UserCate>(UserCate.class));

끝~