Develop/spring-data

[JPA] JPA를 적용하며.. ( field convert 편 )

에디개발자 2020. 11. 30. 07:00
반응형

오늘도 JPA를 적용한 프로젝트를 진행 중입니다. 지난글 [JPA] JPA를 적용하며.. ( insert, update 분기 편 ) 에서는 insert, update 분기를 if문 없이 처리하는 유틸리티 함수를 적용했습니다.  이번글에서는 Entity의 field를 convert하는 방법에 대해서 정리하겠습니다.

나를 닮았다고 한다....

사건 전말

Database Table 컬럼 중 동적으로 변하는 데이터가 있었습니다. 우리는 해당 컬럼을 varchar 타입으로 주고 Json 방식으로 저장하기로 하였습니다. 아래에 관련 table과 필드 ddl입니다.

create table staff
(
    id         bigint auto_increment
        primary key,
    age        int          null,
    name       varchar(255) null,
    meta_json  varchar(500) null	// json으로 저장될 필드
);

 

meta_json 필드에 저장될 데이터 예시입니다.

[{
    "meta_name": "name1",
    "meta_type": "type1"
},{
    "meta_name": "name2",
    "meta_type": "type2"
},{
    "meta_name": "name3",
    "meta_type": "type3"
}]

불편하다. 귀찮다

그럼 Java에서 meta_json 데이터를 조회할 때마다 JSON 타입을 List<Vo> 로 컨버트해서 쓰면 되겠다!  

불편하다!! 협업하는 프로젝트이기 때문에 개발자1은 HashMap으로 가져오고 개발자2는 Vo로 가져오고.. 이걸 방지하기 위해서 Vo만들어놨어요 Convert 공통 메서드 만들어놨어요 이거 쓰세요. json 필드를 만들때마다?? 이럴순 없다!!

협업에서 중요한 건 개발자들의 My way보단 공통화된 소스로 작업하는 것이 중요하다고 생각합니다. 
각자 다른 스타일로 만들면 내가 작성한 외 코드를 볼 때 새로 분석을 해야합니다. 그사람의 디자인 패턴 분석...
소스가 같은 패턴으로 작성되면 유지보수할 때에도 소스가 눈에 잘 들어올 것이고 그럼 당연히 분석도 쉬울 것입니다. 

 

해답

그래서 구글링으로 해답을 찾았습니다. @Convert를 Entity json 필드에 선언하면 끝! 사용하기도 쉽고 모든 개발자가 Entity 필드 타입만 보아도 공통되게 쓸 수 있을 것입니다.

 

적용

먼저 Json 타입의 데이터와 매핑이 가능한 Vo를 생성합니다.

@Getter
@Setter
@ToString
public class StaffMetaVo {
    private String metaName;
    private String metaType;
}

 

다음으로 디비의 데이터를 컨버터하는 클래스를 생성합니다.

@Slf4j
@Converter
public class MetaListConverter implements AttributeConverter<List<StaffMetaVo>, String> {

    @Override
    public String convertToDatabaseColumn(List<StaffMetaVo> list) {
        if(CollectionUtils.isNullOrEmpty(list)) {
            return new String();
        }

        try {
            ObjectMapper objectMapper = CommonUtils.getMapper();
            return objectMapper.writeValueAsString(list);
        } catch (JsonProcessingException e) {
            log.info("failed to parse database. data to json.");
            return new String();
        }
    }

    @Override
    public List<StaffMetaVo> convertToEntityAttribute(String dbData) {
        try {
            ObjectMapper objectMapper = CommonUtils.getMapper();
            return Arrays.asList(objectMapper.readValue(dbData, StaffMetaVo[].class));
        } catch (JsonProcessingException e) {
            log.info("failed to parse database. data to json.");
            return Collections.emptyList();
        }
    }
}
  • @Converter를 선언합니다.
  • AttributeConverter를 implements 합니다. <변환될 타입, 변환할 타입>
  • ObjectMapper를 이용하여 convert합니다.

 

마지막으로 Entity 객체에 convert 대상이 될 필드에 @Convert를 붙혀줍니다.

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PUBLIC)
public class Staff {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String address;
    
    @Convert(converter = MetaListConverter.class)
    private List<StaffMetaVo> metaJson;

    @Builder
    public Store(Long id, String name, String address, List<StaffMetaVo> metaJson) {
        this.id = id;
        this.name = name;
        this.address = address;
        this.staff = staff;
        this.metaJson = metaJson;
    }
}

작성된 코드로 metaJson 필드 조회하면 List<StaffMetaVo> 타입으로 Java에서 사용할 수 있습니다. 반대로 Java에서 List<StaffMetaVo> 타입으로 사용하고 save하면 String 타입으로 변환되어 DB에 저장됩니다.

이로써 Entity를 사용하는 개발자들은 일관성있게 metaJson 필드를 사용하려면 List<StaffMetaVo> 타입을 사용하게 되었습니다!! 

 

p.s) "불편하고 귀찮다." 에서 끝나지않고 "개선해보자" 라는 생각으로 임하면 나도 성장하고 동료들도 성장하고 블로그를 읽는 분들도 성장할 수 있다고 생각합니다 :) 
반응형