엔티티의 열거형 칼럼 변환

Dierslair·2022년 5월 21일
0

jpa

목록 보기
1/4

자바의 열거형 타입은 엔티티의 분류 코드나 상태 값을 저장하기에 적합한 타입입니다.

Boolean 이나, Int 타입으로 저장해도 사용상에 문제는 없지만 열거형을 사용하게 되면 사람이 이해하기 쉽습니다.

// 결제 정보
@Entity
class Payment : AbstractEntity() {
  ...
  // 결제 상태를 정의합니다.
  @Enumerated(EnumType.STRING)
  @Column(name = "paymentType", length = 64, nullable = false)
  var type: PaymentType? = null
}
// 결제 상태
enum class PaymentType(
  private val messageCode: String,
) {
  INITIALIZED("enum.payment.type.initialized"), // 주문서 작성 완료
  PENDING("enum.payment.type.pending"), // 결제 대기
  SUCCESS("enum.payment.type.success"), // 결제 성공
  FAILURE("enum.payment.type.failure"), // 결제 실패
  CANCELLED("enum.payment.type.cancelled"), // 결제 취소
  REJECTED("enum.payment.type.rejected"), // 결제 반려됨
  EXPIRED("enum.payment.type.expired"), // 결제 시간 만료됨
  ;
}

편의상 열거형 타입의 데이터를 데이터베이스에 저장하기 위해서 @Enumerated 애노테이션으로 이름을 문자열로 저장할 지(EnumType.STRING), 열거형의 순서로 저장할 지(EnumType.ORDINAL) 지정하여 관리합니다만,
이는 몇 가지 문제점을 가집니다.

  • 열거형의 이름으로 저장하는 경우, DB를 직접 조작하는 경우 실수로 오타를 내거나 코드를 작성하고 난 후 시간이 지나 열거형의 멤버 이름이 변경되는 경우 DB쪽에서도 이를 함께 변경해 주어야 함
  • 열거형의 순서로 저장하는 경우, 열거형의 멤버를 추가하거나 삭제할 때 필연적으로 순서가 바뀌기 때문에 그 의미가 변함

특히, 이름이 변경되는 경우가 잦은데 개발/배포 DB가 별도로 운용되고 브랜치가 다수 운용되는 경우 DB에서 값을 불러올 때 열거형 타입의 데이터를 변환하는 데 실패하여 시스템 장애로 이어집니다.

따라서 불편함은 있지만 @javax.persistence.Convert 애노테이션을 사용하여 컨버터를 직접 지정하여 열거형 타입의 데이터를 직접 변환하는 것이 장애 발생을 최소화할 수 있습니다.

가령

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
enum class PaymentType {
  INITIALIZED("enum.payment.type.initialized"),
  ...
  ;
  
  @JvmStatic
  @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
  fun parse(name: String?): PaymentType? =
    name?.let { EnumUtils.getEnumIgnoreCase(PaymentType::class.java, it.trim()) }
    
  // 해당 열거형 타입의 컨버터 클래스를 내부에 정의합니다.
  class Converter : AttributeConverter<PaymentType, String> {
    override fun convertToDatabaseColumn(attribute: CmsHistoryType?): String? =
      attribute?.name

    override fun convertToEntityAttribute(dbData: String?): CmsHistoryType? =
      dbData?.let { CmsHistoryType.parse(it) }
  }
}

열거형 타입의 클래스 내부에 컨버터를 작성하고, @Enumerated 애노테이션을 @Covert 애노테이션으로 변경합니다.

@Entity
class Payment : AbstractEntity() {
  ...
  @Convert(converter = PaymentType.Converter::class)
  @Column(name = "paymentType", length = 64, nullable = false)
  var type: PaymentType? = null
}

아쉬운 점은 엔티티에서 열거형 타입을 사용할 때 마다 컨버터를 사용해야 한다는 점입니다.

profile
Java/Kotlin Backend Developer

0개의 댓글