221127 TIL 검색 기능 구현 중 만난 문제 (pagination)

오늘은 어제 게시판 검색 기능을 간단하게 구현했던 것에 페이지네이션 기능까지 추가하려 하는데 발생한 문제와 해결 방법을 적어 보려 한다.

문제 상황

게시글 제목에 "검색"이라는 키워드가 포함된 게시물을 찾는데 총 11개가 존재할 경우 한 페이지에 10개가 출력이 돼야 하는데 9개만 출력이 되는 문제가 있었다.

근데 이 문제가 모든 게시물이 있는 전체 게시판에서는 발생하지 않는데 특정 카테고리의 게시물만 있는 게시판에서만 문제가 발생했었다.

왜 모든 게시물이 있는 전체 게시판에서는 정상적으로 10개씩 검색 결과가 출력되는데 특정 게시판에서만 10개가 아닌 9개만 출력이 되는지 의아했었다.

아래는 게시물을 search 하는 controller이다.
페이지 네이션을 위한 Pageable객체와 어떤 게시판인지 알기 위한 boardId값, 검색어인 keyword, 어떤 종류(제목만, 내용만, 작성자)로 게시물을 검색하는지 알기 위한 keywordType을 받는다.

 

Pageable의 page 사이즈는 기본값인 10으로 설정되어 있는데도 "검색"키워드를 검색한 게시물의 총개수는 9였다...

 


우선 PostRepository에서 게시물을 찾아오는 로직은 아래와 같다.

public PostsDto search(Long boardId, String keyword, String keywordType, Pageable pageable) {
    List<PostDto> searchedPosts = searchByPostTitle(keyword, pageable)
        .stream().filter(post -> post.getBoardId().equals(boardId))
        .map(Post::toDto).collect(Collectors.toList());
}

private Page<Post> searchByPostTitle(String keyword, Pageable pageable) {
     return postRepository.findByPostInformation_TitleContaining(keyword, pageable);
}

1. postRepository에서 post의 title 칼럼에서 전달받은 keyword("검색")이 포함된 게시물 전부를 찾는다
2. 특정 키워드가 포함된 모두 게시물에서 검색어를 입력한 게시판의 게시물만 출력이 돼야 하니까 전달받은 게시판의 아이디(boardId)를 이용해 자신이(post) 갖고 있는 게시판의 아이디와 비교해 같은 것들만 dto로 변환해 list로 만든다.

어디서 게시물 하나가 빠졌는지 찾기 위해서 서비스단에서 의심이 되는 부분 곳에 system.out.print를 하나씩 찍어봤는데 이상한 곳 한 곳을 찾을 수 있었다.

searchByPostTitle 메서드에서 findByPostInformation_TitleContaining메서드를 사용해서 불러오는 post들을 하나씩 출력해봤는데 이상 없이 10개 모두가 출력되는 것을 확인했다.
이후 filter이후의 post들을 출력해봤는데 총게시물이 9개였었다. 즉 filter를 해올 때 post하나가 걸러져서 출력이 되고 있었던 것이었다..

filter 쪽 로직을 다시 살펴보니 post 1개가 걸러져서 출력된 이유를 납득할 수 있었다.
keyword가 포함된 모든 게시글(여기선 10개)을 불러온 다음에 다시 전달받은 boardId와 비교해 같은 것들만 list에 담으니까 같지 않은 것 1개가 걸러지고 있었던 것이었다.

나는 특정 게시판에서 keyword가 포함된 게시물을 찾는 것이 목표였기 때문에 위와 같은 방법을 생각했던 것이었는데 전혀 잘못된 방법이었다.

위 방법처럼 모든 게시물에서 찾는 것이 아니라 특정 게시판에서의 게시물 중 keyword가 포함된 게시물을 찾는 방법이 없을까 생각하다 jpa repository의 메서에서 and를 사용해서 검색할 수 있다는 것을 알게 되었다.

 

https://docs.spring.io/spring-data/jpa/docs/1.10.1.RELEASE/reference/html/#jpa.sample-app.finders.strategies


그래서 findByPostInformation_TitleContaining에 전달받은 boarId를 메서드에 and를 이용해 아래와 같은 메서드를 만들었다.

List<PostDto> searchedPosts = postRepository
    .findByBoardIdAndPostInformation_TitleContaining(boardId, keyword, pageable)
    .stream().map(Post::toDto).collect(Collectors.toList());

전달받은 boardId와 keyword 둘 다 모두 갖고 있는 post를 찾을 수 있었다.

 

이번엔 정상적으로 boardId가 2(EPL게시판)이고 제목에 "검색"이 포함되어있는 게시물 10개 모두 찾아올 수 있었다.