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을 써야하나..

    댓글