[Spring] DB 코드값과 Enum의 매핑 방법 @Enumerated vs @Convert

푸드테크·2022년 7월 3일
2
post-thumbnail
post-custom-banner

안녕하세요 푸드 테크팀 백엔드 개발자 박형민입니다

이번 포스팅에서는 팀 내 온보딩 중, 새롭게 알게된 @Convert 에 대해서 이야기해보고자 합니다.


Spring을 하다보면, Entity의 필드를 Enum 값으로 다룰 때가 있습니다.

Enum 값으로, 필드의 상태값을 다루어서, 값을 제한한다고 해도, 데이터베이스에는 상태코드로 저장된다거나,

아니면, DB에서 String 값으로 다루던걸, java에서는 String to Enum 으로 다루고 싶을 때가 있는데 오늘은 이 방법에 대해서 포스팅해보고자 합니다.

(디비에서는 상태코드(Or String) 값으로, 자바에서는 Enum 객체로)





Enum to String

먼저, 간단하게 자바에서 Enum 으로 다룬단걸, DB 에 저장할 때, 문자열로 바꾸는 방법입니다.

  1. name()
  2. toString()

Enum.class 내부에 정의된 2 메소드를 이용해서 Enum value를 문자열로 변환할 수 있습니다.

name()과 toSring() 2개의 메소드가 정의되어 있지만, 내부적으로는 toString에 접근하여 사용하는 것을 권장하고 있습니다.




String to Enum

Java 에서 DB에 저장된 문자열 값을, Enum 클래스의 값으로 변환하는 몇 가지 방법이 있습니다.

  1. Enum.valueof()

  2. @Convert 와, AttributeConverter를 상속받는, Converter 클래스를 구현해서 받는 방법

  3. @Enumerated(EnumType.STRING) 어노테이션


위 3가지 방법을 설명하기 전에, 먼저 주문 상태를 나타내는 OrderStatus Eunm.class 를 만들어 예시를 들어보겠습니다.

public enum OrderStatus {
    WAIT,		// 주문 대기
    COMPLETE,	// 주문 완료
    CANCEL		// 주문 취소
}

1) Enum.valueOf()

첫번째 방법은, Enum.class에 정적 팩토리 메소드로 구현된 valueOf(Enum.class, String) 메소드를 이용하는 방법입니다.

  1. Enum.valueOf(내가만든Enum.class, 매핑할String) 으로 사용하거나
    @Override
    public OrderStatus convertToEntityAttribute(String dbData) {
      return Enum.valueOf(OrderStatus.class, dbData);
    }
  2. 구현된 Enum.class 에 매핑할 문자열만 매개변수로 넘겨주어 사용할 수 도 있습니다.
    @Override
    public OrderStatus convertToEntityAttribute(String dbData) {
      return OrderStatus.valueOf(dbData);
    }

단순 구현된 메소드를 사용하니 들어오는 문자열에 대한 유효성 체크는 어떻게해야하나~ 걱정이 들 수 있지만,

구현된 정적 팩토리 메소드 안에서, Null 체크와, 문자열 값 매핑에 대한 익셉션 처리가 구현되어 있었습니다.




2) @Converter 와 @Convert

두번째 방법은, Enum to String, Stromg to Enum 메소드를 정의하는 AttributeConverter 인터페이스를 상속받는 Converter 구현체를 만드는 것입니다.

AttributeConverter

다이어그램을 열어서 봐도, 어떠한 클래스에도 상속되거나 상속받지않는 딱, 정의된 2개의 메소드만을 위한 인터페이스 인 것 같습니다.

AtrributeConverter를 implemets한 클래스는 위 사진의 2함수를 오버라이딩하게 되어있고, 메소드 이름만 봐도 어떤 역할을 하는지 알 수 있도록 되어있습니다.

  • convertToDatabaseColumn(Menu menu) : enum을 DB에 어떤 값으로 넣을 것인지 정의

  • converToEntityAttribute(String menuName) : DB에서 읽힌 값에 따라 어떻게 enum과 매칭 시킬 것인지 정의

    @Converter
    public class OrderStatusConverter implements AttributeConverter<OrderStatus, String> {
    
      @Override
      public String convertToDatabaseColumn(OrderStatus attribute) {
          if (Objects.isNull(attribute)) {
              throw new NullPointerException("Enum Converting String - OrderStatus is null");
          }
    
          return attribute.toString();
      }
    
      @Override
      public OrderStatus convertToEntityAttribute(String dbData) {
          return OrderStatus.valueOf(dbData);
      }
    

여기서, 위에서 사용된 valueOf 메소드 와 toString이 이용됩니다. valueOf() 메소드에서 기본적인 Exception 처리를 해주기 때문에, 따로 핸들링을 해주진 않았습니다.

사실, valueOf만 사용하면 간단하게 Enum 클래스와 문자열의 매핑이 가능한데, 굳이 Converter 클래스를 따로 구현해 주어야하나~ 라는 의문점이 들 수 있습니다만,

JPA에서는 @Convert 어노테이션을 Entity의 프로퍼티에 적용하면, @Converter 어노테이션이 적용된 클래스와 자동으로 매핑하여, DB에 저장된 varChar 데이터타입을 Enum 으로 매핑시켜, Java에서 Enum 클래스로 핸들링 할 수 있도록 해줍니다.

@NotNull
@Convert(converter = OrderStatusConverter.class)
@Column(name = "status")
private OrderStatus orderStatus;

➡️ 이렇게하면, 자동으로 "status" 컬럼에 저장된 varchar 문자열 데이터가, OrderStatus enum.class로 핸들링 할 수 있도록 세팅 되는 것이죠


또한, 단순하게 값을 변환하는게 아닌, 직접 구현한, Converter Class의 메소드를 이용해서 상태값을 변환 시키기 때문에, 조건에 따른 커스텀한 구현도 가능하다는 장점도 있을것 같습니다.





3) Enumerated(EnumType.STRING) 과의 차이점

DB의 문자열을, Entity의 Enum과 매핑하는 또다른 방법인 Enumerated 어노테이션이 있습니다.

JPA는 enum 필드에 @Enumerated(EnumType.STRING) 주석을 추가하면 엔티티를 저장할 때 Enum.name() 값을 사용하여 매핑합니다.




📌 @Enumerated(EnumType.STRING) 와 @Convert의 차이점이 무엇일까요?

결론부터 말하자면,jpa 2.1 이전에는 @Enumerate를 사용하였고, 2.1 이후 @Enumerate 의 한계를 극복하기위해 나온게 @Convert 입니다.

@Enumerated(EnumType.String) 경우는 선언한 상수의 이름이 바뀌게 되면 기존에 저장됐던 데이터 와의 정합성 문제가 있고, 문자열로 선언되어 필요 이상으로 많은 공간을 할당하게 된다는 문제점이 있고 이에 대한 한계를 극복하기 위해 JPA 2.1 이후 부터 도입된 것이 @Converter 입니다.



➡️ 하지만 개인적으로! 보통 사용되는 Enum의 길이가 크게 길지 않으므로, 이런 최적화의 이점보다는, Converter 클래스를 구현하여 단순하게 값을 변환시켜 매핑한다는 것 보다, 규칙을 추가하여 새로운 설정을 해줄 수 있다는 것이 더 크게 와닿는 부분이라고 생각됩니다!

profile
푸드 테크 기술 블로그
post-custom-banner

3개의 댓글

comment-user-thumbnail
2022년 7월 4일

글 잘봤습니다. OrderStatus를 추려주는 시나리오도 봤었으면 좋겠다는 생각이 드네요

1개의 답글
comment-user-thumbnail
2023년 8월 12일

좋은 글 감사합니다
혹시, 스프링 내부에서 함수에 대해 설명한 주석이 한글로 된건 설정인가요 아니면 직접 번역하신건가요?

답글 달기