Enum Converter와 Enumerated 어노테이션
728x90
반응형

타입이나 상태를 구분하기 위해 Enum class를 사용한다. 그리고 enum값을 저장하기 위해 Enum Converter 혹은 Enumerated 어노테이션을 이용하게 된다. 이 둘의 차이는 무엇이며 어떤게 더 선호되는지 확인해보려고 한다.

 

네 enum을 한번 알아봐야겠구나 (깔깔깔)

 

Enumerated 어노테이션

Enumerated 어노테이션은 원하는 필드에 추가하는 것만으로도 (DB와 같은)소스에 String값을 입력해준다. 물론 순서값을 입력해주는 Option인 ORDINAL로 입력이 가능하지만 의도치않게 enum값의 순서가 바뀌는 경우가 생기면 데이터가 꼬이기 때문에 권장하지 않는다.

enum class UserType {
    NORMAL,
    ADMIN;
}

@Column(name = "user_type", length = 70)
@Enumerated(EnumType.STRING)
val userType: UserType

 

그런데 만약에 위 UserType을 아래와 같이 바꾸면 어떻게 될까?

enum class UserType {
    NORMAL,
    // ADMIN;
    MASTER;
}

 

이미 DB에 있는 ADMIN은 찾아올 수 없다. 값은 비즈니스에 의해 언제든 바뀔 수 있다. 그렇다면 절대 바뀌지 않는다고 보장하는 것과 바뀔 수 있는 것을 구분할 수 있다면 enum을 자유자재로 사용할 수 있지 않을까? 아쉽게도 Enumerated 어노테이션으로는 한계가 있다. 이런 경우에 사용할 수 있는 것이 바로 Enum Converter이다.

 

 

Enum Converter

Enum Converter는 Entity의 Enum값을 DB에서 가져올 때 변환하는 방법과 반대로 DB에서 Entity의 데이터를 가져올 때의 변환하는 방법을 정의한 것으로 AttributeConverter 인터페이스와 Converter 어노테이션을 통해 구현이 가능하다.

@Converter
class UserTypeConverter : AttributeConverter<UserType, String> {
    override fun convertToDatabaseColumn(attribute: UserType?): String? {
        if (attribute == null) return null
        return attribute.name
    }
    override fun convertToEntityAttribute(dbData: String?): UserType? {
        if (dbData == null) return null
        return UserType.fromName(dbData)
    }
}
  • convertToDatabaseColumn : Entity의 enum값을 DB에 변환하는 방식을 정의
  • convertToEntityAttribute : DB값을 Entity의 enum값으로 변환하는 방식을 정의

만약 변환과정에 별도의 로직 처리가 필요하다면 이 Converter 구현체에 로직을 추가하면 된다. 그런데 이렇게 쓰려고 보니 Enumerated 어노테이션과 크게 다르지 않아 보인다. Enum이 변경되더라도 유연하게 대처할 수 있게 하려면 Enum에 속성값을 부여하고 해당 속성값에 따라 변환해주도록 Converter 구현체를 만들면 된다.

 

enum class UserType(val value: String) {
    NORMAL("NORMAL"),
    // ADMIN("ADMIN");
    MASTER("ADMIN");
    
    companion object {
        fun fromValue(value: String): UserType {
            return values().firstOrNull {
                it.value == value
            } ?: throw IllegalArgumentException("Format $value is illegal")
        }
    }
}

@Converter
class UserTypeConverter : AttributeConverter<UserType, String> {
    override fun convertToDatabaseColumn(attribute: UserType?): String? {
        if (attribute == null) return null
        return attribute.value
    }
    override fun convertToEntityAttribute(dbData: String?): UserType? {
        if (dbData == null) return null
        return UserType.fromValue(dbData)
    }
}

이렇게 사용하면 name이 변경되더라도 value는 유지하는 방식으로 DB에 영향을 주지 않으면서 원하는 형태로 Enum값을 수정할 수 있다. 단순히 값을 유연하게 사용하는 것 뿐만 아니라 DB값에서 여러 경우의 수를 처리하고 싶을 때도 위 샘플 코드처럼 오버라이드 메소드에 필요한 로직을 삽입하면 된다. 매우 단순하게 보여줬는데 우아한형제들 기술 블로그에는 더 자세히, 더 완벽하게 설명하고 있다. 추천추천.

 

참고자료
- Legacy DB의 JPA Entity Mapping (Enum Converter 편)
- AttributeConverter를 이용해 DB에 값 그대로 저장하기
- JPA Entity 클래스에 Enum 타입 사용기
- JPA ENUM 사용법

.

728x90
반응형