jpa로 게시판을 구현하던 중 검색을 어떻게 처리할지 고민을 많이 해봤다.
처음에는 mybatis를 통해서 일일히 하나씩 넣으려고 했었다.
예를 들어서 아래 방법처럼 필드마다 지정하려고 했는데 다른 방법이 있나 찾아봤다.
< if test="">AND KEYWORD = #{KEYWORD}</if>
@Query("SELECT b FROM Board b WHERE b.category IN :categories AND (b.title LIKE %:keyword% OR b.content LIKE %:keyword%)")
List<Board> searchList(@Param("categories") List<Category> categories, @Param("keyword") String keyword);
JPQL은 SQL과 비슷한 구문으로 복잡한 조건을 쿼리로 직접 풀어낼 수 있는 장점이 있다.
또 @Qurey 어노테이션을 사용해서 작성하기 때문에 유지보수도 쉽고, 코드가 간결해진다.
하지만 저렇게 직접 쿼리를 짜는 것은 마이바티스와 다른점이 없다고 생각이 들었고,
장점으로 유지보수가 좋다고는 하지만 더 길어지게 되면 가독성이 떨어질 것 같다고 생각했다.
실제로도 런타임 오류 가능성과 복잡성 증가 등의 단점이 있다고 한다.
그래서 다른 방법을 찾던 중 Containing 에 대해 알게되었다.
메서드 이름으로 쿼리를 정의할 때 사용되는 키워드 중 하나로, 특정 문자열을 포함하는 항목을 검색하는 데 사용된다. Containing은 SQL의 LIKE 연산자와 유사하며, 기본적으로 부분 문자열 매칭을 지원한다.
여기서 내가 사용하겠다고 마음 먹은 점은 LIKE %keyword%로 동일하다는 점이고, 다중 필드를 검색할 수 있다는 점이다. 그래서 직접 코드를 작성해봤다.
List<Board> findByCategoryInAndTitleContainingOrContentContaining(List<Category> categories, String title, String content);
내가 원했던 조회는 category 2개와 검색 키워드가 제목 혹은 내용에 있다면 조회가 되어야다.
그래서 in 연산자를 사용했다. 실제 서비스는 아래와 같다.
/**
* 게시글 전체 검색(공지사항, 자주찾는 질문 포함)
* @param keyword
* @return
*/
public List<BoardDto> searchMainBoardList(String keyword) {
List<Category> findCate = List.of(
entityFetcher.selectCategory(1L),
entityFetcher.selectCategory(2L)
);
List<Board> boards = boardRepository.findByCategoryInAndTitleContainingOrContentContaining(findCate, keyword, keyword);
return boards.stream()
.map(boardMapper::boardToBoardDTO)
.collect(Collectors.toList());
}
이렇게 작성한 후 keyword 하나만 서버로 보내주면 원하는 보드 리스트를 받을 수 있다.
하지만 네이밍 메서드가 너무 길어지면 그것도 가독성이 떨어지니...
그때는 쿼리 dsl을 도입도 고민하고 있다.
오늘도 공부 끝!