ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [SpringBoot] JPA Entity와 데이터베이스 간 Enum 매핑
    Back-end/TIL 2022. 4. 12. 08:58

    스타트업 백엔드 인턴으로 근무하며 배운 내용을 까먹지 않도록 하기 위해 정리하는 글입니다.

     

    회사에서 약관 관련 업무를 진행하며 약관 필드의 타입이 Enum으로 설정되어 있었고, 이를 계기로 Enum 저장 및 반환에 대해 찾아보았다.

     

    📌 Enum이란 무엇인가

    기존에 상수를 정의할 때 사용하던 final static String 방식을 개선하여 나온 것으로, Enum은 열거형이라고 불리며, 서로 연관된 상수들의 집합을 의미한다.

     

    📌 Enum 타입의 Entity 필드를 데이터베이스에 저장하는 방법

    방식1. @Enumerated(value = EnumType.{...})

    • ORDINAL - 해당 Enum의 index 값이 DB에 저장된다.
      • 단점
        • Enum 클래스에 정의해둔 Enum 값들의 순서가 바뀔 경우 원하는 데이터를 잘못 읽어오는 문제가 발생한다.

     

    • STRING - 해당 Enum의 텍스트 값 그대로 저장된다.
      • 단점
        • String을 DB에 저장하기 때문에, int 보다는 메모리 용량을 더 차지한다. (하지만 이는 데이터가 아주 많을 때의 문제이기는 함)
        • Enum 클래스에 정의해둔 Enum 값의 명칭을 코드 상에서만 변경한 경우 데이터베이스로부터 데이터를 읽어올 때 알맞은 Enum 값이 없어 에러가 발생한다. (가장 치명적인 문제)

     

    방식2. Enum에 대한 AttributeConverter 인터페이스를 구현한 Converter 클래스 생성 

    • 기존에 정의되어 있던 Enum 값들의 순서가 바뀌거나 명칭이 바뀌는 경우에 대한 단점을 보완한다.
    • Enum 값을 DB에 저장할 때와 DB에서 읽어올 때(JPA Entity에 매핑할 때) 작동하는 함수를 오버라이딩하는 로직을 추가해줘야 한다.

     

    구현 방식 예시

    • Enum 클래스
    //Enum 클래스
    @Getter
    public enum TermsTypeCd {
    
        ServiceUseTerms("10", "ServiceUseTerms", "서비스 이용약관"),
        PrivacyPolicy("20", "PrivacyPolicy", "개인정보 취급방침")
        ;
    
        private String codeValue;
        private String nameValue;
        private String korNameValue;
    
        TermsTypeCd(String codeValue, String nameValue, String korNameValue) {
            this.codeValue = codeValue;
            this.nameValue = nameValue;
            this.korNameValue = korNameValue;
        }
    
        public static TermsTypeCd enumOf(String codeValue) {
            return Arrays.stream(TermsTypeCd.values())
                    .filter( t -> t.getCodeValue().equals(codeValue))
                    .findAny().orElse(null);
        }
    }
    • Entity의 Enum 필드에 @Converter(..) 설정
    //Entity의 Enum 필드에 @Convert(converter = ..)
    @Convert(converter = TermsTypeCdConverter.class)
    private TermsTypeCd termsType;
    • AttributeConverter 인터페이스를 구현한 Converter 클래스
    //AttributeConverter 인터페이스를 구현한 Converter 클래스
    public class TermsTypeCdConverter implements AttributeConverter<TermsTypeCd, String> {
    
        @Override
        public String convertToDatabaseColumn(TermsTypeCd attribute) {	//DB에 저장
            if(attribute == null) {
                return null;
            }
            return attribute.getCodeValue();
        }
    
        @Override
        public TermsTypeCd convertToEntityAttribute(String dbData) {	//Entity로 반환
            if(dbData == null){
                return null;
            }
            return TermsTypeCd.enumOf(dbData);
        }
    }

     

     

    *추가

    팀장님께서 만약 Enum의 숫자 코드 값을 사용한다면, 해당 코드 값의 범위를 크게 잡아둬야 나중에 추가되더라도 확장성이 좋다고 말씀해주셨다.

     

    📌 SpringBoot에서의 존재하지 않는 Enum 값에 대한 예외 처리

    예시 상황

    @GetMapping("/pharm/terms/{pharmTermsTypeCd}")
    public ResponseEntity<SingleResult<PharmTermsDto.PharmTermsResDto>> findPharmTerms(
    	@PathVariable("pharmTermsTypeCd") PharmTermsTypeCd pharmTermsTypeCd
    ) {
    
            log.debug("[복약 안내문 약관 조회 API CALL], {}", pharmTermsTypeCd);
    
            PharmTermsDto.PharmTermsResDto pharmTermsResDto = pharmTermsService.findPharmTermsByTermsType(pharmTermsTypeCd);
    
            SingleResult<PharmTermsDto.PharmTermsResDto> result = responseService.getSingleResult(pharmTermsResDto);
            return new ResponseEntity<>(result, HttpStatus.OK);
    }
    • 위의 코드에서처럼 PathVariable로 들어온 값을 Enum(여기서는 PharmTermsTypeCd) 값과 매칭 시켜 처리한다.
    • 전달된 값에 해당하는 Enum 값이 존재하지 않는 경우, 기본으로 MethodArgumentTypeMismatchException 예외를 반환한다.

     

    📌 추가) 내가 진행한 존재하지 않는 Enum 값에 대한 예외 처리 방안

    상황

    Enum 값을 Path Variable로 받아서 처리하는 API 구현을 하게 되었다. 이때 존재하지 않는 Enum 값에 대해서는 어떻게 처리할 것인지 고민해보았다.

     

    방식1. Enum 타입 자체로 받아서 스프링부트에서 예외 처리 (바로 위에서 언급한 내용)

    • 기본으로 MethodArgumentTypeMismatchException 예외(500 Server Exception)를 발생시킴
    • 구체적인 예외 내용 반환 어려움 (Enum 외 다른 경우에도 해당 예외로 처리되기 때문에)

     

     

     

    방식2. String으로 받고 Enum으로 변환할 때 예외 처리

    • ‘해당 약관이 존재하지 않는다’는 구체적인 예외 내용 반환 가능
    • try-catch문으로 처리해줘야 함

     

    위의 2가지 방식이 떠올랐고, 팀장님께 여쭤보니, 우선 상황에 따라 다를 순 있지만, 지금 상황에서는 Enum 값으로 받을 수 있는데, 방식2처럼 String으로 받는 것은 불필요하다고 말씀해주셨다.

     

    또한, 사용자에 의한 요청에 500 에러를 반환하는 것은 옳지 않으므로 반드시 예외 처리를 해줘야 하지만, 현재 구현하고자 하는 약관 조회 API는 프론트에 의한 요청이므로 예외 처리를 필수로 해 줄 필요는 없다고 하셨다.

     

    만약, 방식1에서 구체적인 예외 메시지를 반환해주고 싶다면, 발생한 특정 오류에 대한 메시지를 잡아서 처리해주도록 하는 방안도 있다.

     

     

     

    🎈참고

    댓글

Designed by Tistory.