백엔드/Java

[이넘아 놀자] 4. @Converter 사용하기

꾸준함의 미더덕 2023. 9. 4. 07:00

저번 게시글에서 DB 저장 시 원하는 코드 값이 아닌 숫자값이 그대로 DB에 저장되고 있는 이슈를 확인해보았습니다.

이번엔 Enum을 활용하여 개발자의 의도대로 데이터를 insert하는 방법을 알아보겠습니다.

 

 

1. 의도대로 Insert 되지 않고 있는 필드들.. - @Id, @Enumerated

 

 

 

@Entity
@Table(name = "post")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@IdClass(PostId.class)
public class Post {
    @Id
    @Enumerated(EnumType.STRING) // 왜 적용 조차 안되지..
    @Column(name = "category_code") 
    private CategoryCode categoryCode;

    @Id
    @Enumerated(EnumType.STRING) // 왜 적용 조차 안되지..
    @Column(name="some_code")
    private SomeCode someCode;
    
    @Enumerated(EnumType.STRING) // 코드값으로 적용은 어떻게 하지..
    @Column(name = "expenditure_code")
    private ExpenditureCode expenditureCode;
    
    
    //...

 

 

 

일단 우리는 위의 코드와 DB 결과를 통해 문제 상황을 대략 이렇게 정리해볼 수 있겠습니다.

 

문제 1) @Id애노테이션과 @Enumerated애노테이션은 같이 적용되지 않는다.

문제 2) @Enumerated(EnumType.STRING)은 단순히 Enum의 필드 name을 삽입시키는 역할을 한다.

 

 

아래에서 이를 검증해보겠습니다.

 


 

 

해결 방안) @Converter를 통한 Enum 코드값 컨버팅


여기서 필요한 것이 JPA가 제공해주는 
jakarta.persistence.Converter 입니다.

 

위의 설명을 보시면 Converter는  @Id와, @Enumerated와는 함께 적용되지 않는다는 설명을 볼 수 있네요 !
현재의 어플리케이션 상황 (복합키 사용, DB 코드값 사용)에 맞지 않는 방법을 적용했던 것입니다..!

 

그럼 @Converter를 사용해볼까요?

@Converter(autoApply = true) // autoApply 옵션이 없으면 엔티티 필드에 @Convert를 각각 선언하여 적용
public class SomeCodeConverter implements AttributeConverter<SomeCode, String> {
    @Override
    public String convertToDatabaseColumn(SomeCode attribute) {
        // DB에 저장 시 Enum -> 코드 변환
        return attribute != null ? attribute.getCode() : null;
    }

    @Override
    public SomeCode convertToEntityAttribute(String dbData) {
        if (dbData == null) {
            return null;
        }

        return Arrays.stream(SomeCode.values())
                // 프로그램에서 조회 시 코드 -> Enum 변환
                .filter(someCode -> someCode.getCode().equals(dbData))
                .findFirst()
                .orElse(null);
    }
}

 

 


cf) 실수로 복합키 설정 클래스의 필드에 @Id 를 붙이지 마세요!
위의 문서에서 보았듯, Converter와 함께 적용되지 않습니다. @Id는 엔티티 필드에 붙이는 것으로 충분합니다.

마찬가지로, Id클래스에 적용한 Enum이 Converter가 필요 없는 경우 해당 Id클래스에서 @Enumerated(EnumType.STRING)을 적용해주세요! 

 


@Getter
@AllArgsConstructor
@NoArgsConstructor
public class PostId implements Serializable {
    //@Id
    @Column(name = "category_code")
    private CategoryCode categoryCode;
    //@Id
    //@Enumerated(EnumType.STRING) 만약 컨버터 사용하지 않는 경우 이곳에 Enumerated 설정하기 
    @Column(name="some_code")
    private SomeCode someCode;
}


//..
public class Post {
    @Id
    //@Enumerated(EnumType.STRING) 어차피 @Id와 @Enumerated는 중복 적용 안됨
    @Column(name = "category_code")
    private CategoryCode categoryCode;

    @Id
    //@Enumerated(EnumType.STRING) 어차피 @Id와 @Enumerated는 중복 적용 안됨
    @Column(name = "some_code")
    private SomeCode someCode;

    //@Enumerated(EnumType.STRING) @Enumerated가 @Converter 보다 우선 적용이라 지워야함!!
    @Column(name = "expenditure_code")
    private ExpenditureCode expenditureCode;

//..

 

 

각각의 EnumConverter를 생성해준 후 다시 테스트 코드를 돌려보겠습니다.

 

    @Test
    public void 포스트_저장_테스트(){
        Post post = Post.builder()
                .categoryCode(CategoryCode.CUSTOMER_INQUIRY)
                .someCode(SomeCode.THING)
                .expenditureCode(ExpenditureCode.FOOD)
                .title("테스트 제목")
                .content("테스트 내용")
                .createdAt(LocalDateTime.now())
                .deleteType(DeleteType.N)
                .build();
        Post savedPost = postRepository.save(post);

        assertThat(savedPost.getCategoryCode()).isEqualTo(CategoryCode.CUSTOMER_INQUIRY);
    }

 

기대했던 코드값이 SQL문으로 생성됨

 

 

이제야 기대했던 코드값이 제대로 insert 되고 있는 것을 확인해볼 수 있었습니다 !!

 

이번 시리즈 글은 이 포스트에서 마무리 지어보겠습니다.

 

감사합니다~!