검색 기능에서 QueryMethod 사용 시 마주한 문제와 선택 - fitpassTeam/fitpass GitHub Wiki
처음 검색 기능을 구현할 때는 @Query를 사용하여 직접 JPQL 쿼리문을 작성하는 방식으로 데이터를 조회하고 있었습니다. 하지만 개발을 진행하면서 JPA에서는 @Query 외에도 QueryDSL, Query Method 등 다양한 방법이 있다는 것을 알게 되었고, 이 중 Query Method를 사용하면 보다 간결하고 직관적인 코드 작성이 가능하다는 장점을 알게 되었습니다.
막연하게 “모든 쿼리를 Query Method로 대체할 수 있지 않을까?” 하는 생각으로 리팩토링을 시도하게 되었고, 실제로 일부 단순한 쿼리는 무리 없이 전환할 수 있었습니다.
예를 들어 아래 두 개의 쿼리는 Query Method로 비교적 쉽게 변경할 수 있었습니다.
@Query("SELECT g FROM Gym g WHERE g.name LIKE %:keyword% AND g.deletedAt IS NULL")
Page<Gym> findByGymName(@Param("keyword") String keyword, Pageable pageable);
@Query("SELECT p FROM Post p WHERE p.gym.id = :gymId AND p.postType = :postType ORDER BY p.createdAt DESC")
List<Post> findByGymIdAndPostType(@Param("gymId")Long gymId, @Param("postType") PostType postType);
하지만 다음과 같은 복잡한 조건이 포함된 쿼리를 마주하며 문제에 부딪혔습니다.
@Query("SELECT p FROM Post p WHERE p.postStatus <> 'DELETED' AND (p.title LIKE %:keyword% OR p.content LIKE %:keyword%)")
Page<Post> searchByTitleOrContent(@Param("keyword") String keyword, Pageable pageable);
이 쿼리를 Query Method로 옮기려는 과정에서 구조적인 한계가 있다는 사실을 알게 되었습니다.
Query Method의 경우 조건을 메서드 이름만으로 표현해야 하기 때문에 복잡한 논리식에 한계가 있습니다.
예를 들어 위 쿼리를 Query Method로 작성하면 아래와 같은 형태가 가능합니다:
Page<Post> findByPostStatusNotAndTitleContainingOrContentContaining(PostStatus postStatus, String keyword1, String keyword2, Pageable pageable);
그러나 이 메서드는 다음과 같이 해석됩니다:
(postStatus != 'DELETED') AND title LIKE %keyword% OR content LIKE %keyword%
즉, AND 조건과 OR 조건이 정확히 괄호로 그룹핑되지 않아, 논리적 의도가 다르게 해석됩니다. 원래의 의도는 아래와 같았습니다:
postStatus != 'DELETED' AND (title LIKE %keyword% OR content LIKE %keyword%)
하지만 위와 같은 Query Method에서는 이를 명확히 표현할 수 없습니다. 이로 인해 content에 키워드가 포함되어 있기만 해도 postStatus가 'DELETED'인 게시글이 검색 결과에 포함될 수 있는 문제가 생깁니다.
이러한 한계로 인해 결국 해당 쿼리는 Query Method로 완전히 대체하는 것이 어렵다는 결론을 내렸습니다. 대신, 복잡한 논리 조건이 필요한 쿼리의 경우에는 기존처럼 @Query를 사용하는 방식을 유지하기로 했습니다.
Query Method는 간단한 조건의 조합이나 자주 사용하는 패턴에 대해 매우 유용하다.
그러나 조건 간의 연산자 우선순위(AND, OR)나 괄호로 묶은 논리식과 같이 복잡한 쿼리를 표현하기엔 구조적인 한계가 있다.
따라서 모든 쿼리를 Query Method로 일괄적으로 바꾸기보다는, 상황에 따라 적절한 방법을 선택해야 한다.
단순 조건: Query Method
복잡한 조건, 동적 쿼리: @Query 또는 QueryDSL
쿼리 작성 방식은 하나만 고집할 필요 없이, 요구되는 조건의 복잡도에 따라 전략적으로 선택해야 한다는 중요한 교훈을 얻었습니다. 이번 경험을 통해 팀원들과도 다양한 쿼리 작성 방식을 공유하고, 유지보수성과 가독성을 함께 고려하는 설계를 진행할 수 있게 되었습니다.