JPQL은 쉽게 깨질 수 있다. 패키지 구조를 변경하며

패키지 구조를 개선하는 리팩토링 과정에서 JPQL로 작성된 부분들에 문제가 발생했습니다.

바로, JPQL 쿼리에서 DTO 클래스 경로 참조가 깨지는 현상인데, 뭐 문자열이니까 어찌보면 당연한 결과기도 합니다.

 

짧게 설명해보겠습니다. 

문제 상황

계층형 패키지 구조에서 도메인 중심 패키지 구조로 전환하면서 아래와 같은 JPQL 쿼리가 동작하지 않았습니다:

@Query("SELECT new com.appcenter.timepiece.dto.project.ProjectThumbnailResponse(p, c.thumbnailUrl) " +
        "FROM Project p " +
        "INNER JOIN MemberProject mp ON mp.projectId = p.id " +
        "LEFT JOIN Cover c ON p.coverId = c.id " +
        "WHERE mp.memberId = :memberId " +
        "AND p.isDeleted = false AND mp.isStored = false " +
        "ORDER BY p.updatedAt DESC")
Page<ProjectThumbnailResponse> fetchProjectThumbnailResponse(Pageable pageable, Long memberId);

ProjectThumbnailResponse 클래스가 com.appcenter.timepiece.dto.project 패키지에서 com.appcenter.timepiece.domain.project.dto 패키지로 이동했기 때문입니다.

 

원인

JPQL 쿼리는 문자열로 정의되며, 이 문자열에는 클래스의 전체 경로(FQCN)가 하드코딩되어 있습니다. 따라서 IDE의 자동 리팩토링 기능이 이 부분을 감지하지 못합니다.

 

해결 방법

모든 JPQL 쿼리를 수동으로 업데이트하여 새 패키지 경로를 반영해야 합니다:

@Query("SELECT new com.appcenter.timepiece.domain.project.dto.ProjectThumbnailResponse(p, c.thumbnailUrl) " +
        "FROM Project p " +
        "INNER JOIN MemberProject mp ON mp.projectId = p.id " +
        "LEFT JOIN Cover c ON p.coverId = c.id " +
        "WHERE mp.memberId = :memberId " +
        "AND p.isDeleted = false AND mp.isStored = false " +
        "ORDER BY p.updatedAt DESC")
Page<ProjectThumbnailResponse> fetchProjectThumbnailResponse(Pageable pageable, Long memberId);

 

JPQL은 편리해서 자주 써왔는데 이러한 문제를 마주한 건 또 처음이라 기억할 겸 적어봤습니다.

패키지 구조 변경 시 이러한 하드코딩된 참조들을 신경써줘야 하는데,, 

음.. 역시 QueryDSL을 써야하나..

댓글