JPQL enum 관련 오류

Kim jisu·2025년 4월 11일
0

 Debugging Note

목록 보기
20/37

JPA에서 Enum 사용 관련 에러

문제 1: JPQL에서 Enum 필드에 문자열 연산 사용

  • 상황:
    JPQL 쿼리에서 열거형 필드를 대상으로 LIKE 등의 문자열 연산을 시도하면 오류 발생
    예) c.receptionType LIKE %:keyword%
  • 원인:
    receptionType은 Enum 타입이므로 기본 문자열 비교 연산을 사용할 수 없음

해결 방안 1: Enum을 문자열로 저장

  • 방법:
    엔티티에 @Enumerated(EnumType.STRING) 애너테이션을 사용해 Enum 값을 문자열로 DB에 저장
    @Enumerated(EnumType.STRING)
    private ReceptionType receptionType;
  • 장점:
    DB에 저장된 Enum 값이 문자열이므로 JPQL에서 직접 문자열 비교 연산 사용 가능
  • 주의:
    Enum 값이 저장되는 방식이 변경되므로 데이터 마이그레이션이 필요할 수 있음

문제 2: JPQL에서 Enum 필드와 문자열 파라미터 비교

  • 상황:
    JPQL 쿼리에서 Enum 필드와 문자열 파라미터를 비교하는 경우
    예) OR c.receptionType = :keyword
  • 원인:
    Enum 타입과 문자열 사이의 타입 불일치로 인해 비교 연산이 제대로 동작하지 않음

해결 방안 2: 파라미터 타입 일치시키기

  • 방법:
    JPQL 파라미터 타입을 Enum으로 변경하여 직접 비교하거나,
    별도의 변환 함수를 사용하여 Enum 값을 문자열로 변환 후 비교
    • Enum 파라미터 사용 예)
      @Query("SELECT c FROM Competition c WHERE c.isDeleted = false AND (c.title LIKE %:keyword% OR c.receptionType = :keyword)")
      Page<Competition> searchCompetition(@Param("keyword") ReceptionType keyword, Pageable pageable);
  • 대안:
    만약 문자열 검색이 필요한 경우, 엔티티의 Enum 필드를 별도의 문자열 프로퍼티로 관리하여 검색하는 방법도 고려

문제 3: JPQL 파라미터 표기법

  • 상황:
    JPQL 쿼리에서 파라미터를 사용하는 경우, : (콜론) 기호를 붙여야 함
    잘못된 예) OR c.receptionType = keyword
  • 해결:
    올바른 형식은 OR c.receptionType = :keyword
    ※ 항상 파라미터 이름 앞에 콜론(:)을 붙이도록 주의

실제 오류 사례 및 해결 방안

문제 상황

  • 경쟁전(Competition) 검색 API에서 "춘천"이라는 키워드로 검색 시 다음 오류 발생:
    Argument [춘천] of type [java.lang.String] did not match parameter type [com._42195km.msa.competitionservice.domain.model.ReceptionType (n/a)]

오류 분석

  • 문제의 쿼리:

    @Query("SELECT c FROM Competition c WHERE c.isDeleted = false AND (" +
         "c.title LIKE CONCAT('%', :keyword, '%') " +
         "OR c.type =:keyword " +      // CompetitionType enum에 대한 비교
         "OR c.receptionType =:keyword)") // ReceptionType enum과 String 비교
    Page<Competition> searchCompetition(@Param("keyword") String keyword, Pageable pageable);
  • String 타입의 키워드 "춘천"과 Enum 타입인 typereceptionType 필드를 직접 비교하려고 시도하여 타입 불일치 오류 발생

해결 방법 1: JPQL 쿼리 수정

@Query("SELECT c FROM Competition c WHERE c.isDeleted = false AND (" +
       "c.title LIKE CONCAT('%', :keyword, '%') " +
       "OR CAST(c.type AS string) = :keyword " +
       "OR CAST(c.receptionType AS string) = :keyword)")
Page<Competition> searchCompetition(@Param("keyword") String keyword, Pageable pageable);

해결 방법 2: 검색 로직 분리

public Page<CompetitionAppResponseDto> getCompetition(String keyword, Pageable pageable) {
    try {
        Page<Competition> competition;
        
        // 1. CompetitionType enum에 해당하는지 확인
        boolean isCompetitionType = Arrays.stream(CompetitionType.values())
                .anyMatch(type -> type.name().equals(keyword));
                
        // 2. ReceptionType enum에 해당하는지 확인
        boolean isReceptionType = Arrays.stream(ReceptionType.values())
                .anyMatch(type -> type.name().equals(keyword));
        
        if (isCompetitionType || isReceptionType) {
            // enum 타입에 맞는 검색
            competition = competitionRepository.searchByEnumType(keyword, pageable);
        } else {
            // 일반 문자열 검색 (제목만)
            competition = competitionRepository.searchByTitle(keyword, pageable);
        }
        
        return competitionMapper.toAppResponseDtoPage(competition);
    } catch (Exception e) {
        log.error("error check : {}", e.getMessage());
        throw CustomBusinessException.from(CompetitionServiceCode.COMPETITION_GET_FAIL);
    }
}

// Repository 메서드 추가
@Query("SELECT c FROM Competition c WHERE c.isDeleted = false AND c.title LIKE CONCAT('%', :keyword, '%')")
Page<Competition> searchByTitle(@Param("keyword") String keyword, Pageable pageable);

@Query("SELECT c FROM Competition c WHERE c.isDeleted = false AND " +
       "(CAST(c.type AS string) = :keyword OR CAST(c.receptionType AS string) = :keyword)")
Page<Competition> searchByEnumType(@Param("keyword") String keyword, Pageable pageable);

결론

  • 핵심 포인트:
    • Enum 필드를 JPQL에서 문자열 비교 연산으로 사용하려면 반드시 저장 방식이나 비교 대상의 타입을 맞춰야 한다.
    • Enum 값을 문자열로 저장하는 경우 @Enumerated(EnumType.STRING)을 사용하더라도, 문자열 파라미터와 직접 비교할 때는 타입 변환이 필요하다.
    • 가장 안전한 방법은 검색 대상에 따라 쿼리를 분리하여 타입 불일치를 방지하는 것이다.
    • 문자열과 Enum 간의 비교가 필요한 경우, JPQL의 CAST 함수를 사용하거나 Java 코드에서 사전에 타입 변환을 처리하는 방식이 권장된다.
profile
Dreamer

0개의 댓글