슬기로운 개발생활

Spring Boot JPA 게시판 검색 기능 & 검색 페이징 구현

by coco3o
반응형

우리는 만약 게시판에 검색기능이 없다면 찾고자 하는 글을 일일이 찾아야 할것이다.

검색 기능은 꼭 있어야 할 기능이라고 생각해 구현해보려 한다.

우선 검색 기능을 먼저 구현하고, 검색 결과가 많을 경우에 대한 페이징 처리또한 구현하도록 하자.


1. 검색 기능

1-1. Repository

List<Posts> findByTitleContaining(String keyword);

JpaRepository에서 메소드명의 By 이후는 SQL의 where 조건 절에 대응되는데,
위와 같이 Containing을 붙여주면 Like 검색이 가능해진다. 즉, %{keyword}%가 가능하다.
자세한 설명은 여기 를 참고하자.

1-2. Service

    /* search */
    @Transactional
    public List<Posts> search(String keyword) {
        List<Posts> postsList = postsRepository.findByTitleContaining(keyword);
        return postsList;
    }

Repository에서 검색결과를 받아 비즈니스 로직을 실행하는 메소드이다.

1-3. Controller

    @GetMapping("/posts/search")
    public String search(String keyword, Model model) {
        List<Posts> searchList = postsService.search(keyword);

        model.addAttribute("searchList", searchList);

        return "posts-search";
    }

1-4. searchForm (Mustache)

    <form action="/posts/search" method="GET" class="form-inline p-2 bd-highlight" role="search">
        <input type="text" name="keyword" class="form-control" id="search" placeholder="검색">
        <button class="btn btn-success bi bi-search"></button>
    </form>

검색창이다. input에 데이터를 입력하면 폼 데이터를 해당 주소로 GET 해준다.
Controller가 데이터를 받아 지지고 볶은 다음 posts-search에 보낸다.

1-5. posts-search.Mustache

{{>layout/header}}
<div id="posts_list">
    <table id="table" class="table table-horizontal">
        <thead id="thead">
        <tr>
            <th>번호</th>
            <th>제목</th>
            <th>작성자</th>
            <th>등록일</th>
            <th>조회수</th>
        </tr>
        </thead>
        <tbody id="tbody">
        {{#searchList}}
            <tr>
                <td>{{id}}</td>
                <td><a href="/posts/read/{{id}}">{{title}}</a></td>
                <td>{{writer}}</td>
                <td>{{modifiedDate}}</td>
                <td>{{view}}</td>
            </tr>
        {{/searchList}}
        </tbody>
    </table>
    <div class="text-right">
        <a href="/posts/write" role="button" class="btn btn-primary bi bi-pencil-fill"> 글쓰기</a>
    </div>
</div>
{{>layout/footer}}

데이터를 받아 검색 결과를 보여준다.

1-7 검색 테스트 하기

'4'를 검색했을 때 제목에 4가 포함된 글들을 가져오는 걸 확인할 수 있다.

URL

2. 검색 후 페이징 처리

검색처리는 구현했지만 아직 부족한 부분이 많이 있다. 이번 파트에서 전체적으로 완성도를 높여보자.

2-1. Repository

Page<Posts> findByTitleContaining(String keyword, Pageable pageable);

기존 메소드에서 List 타입을 Page 타입으로, 그리고 파라미터에 Pageable을 받도록 했다.

2-2. Service

    /* search */
    @Transactional
    public Page<Posts> search(String keyword, Pageable pageable) {
        Page<Posts> postsList = postsRepository.findByTitleContaining(keyword, pageable);
        return postsList;
    }

Service 도 동일하게 타입을 Page로, 그리고 파라미터에 Pageable만 추가해주면 끝이다.

2-3. Controller

    @GetMapping("/posts/search")
    public String search(String keyword, Model model, @PageableDefault(sort = "id", direction = Sort.Direction.DESC)
           Pageable pageable) {
        Page<Posts> searchList = postsService.search(keyword, pageable);

        model.addAttribute("searchList", searchList);

        return "posts-search";
    }

@PageableDefault 설정과 Pageable 파라미터를 추가해준다.

2-4. 검색 페이징 테스트

'2' 를 검색했더니 페이지 사이즈가 10으로, 그리고 DESC 정렬되어있는 것을 확인할 수 있다.
그럼 나머지 검색 결과는 어디로 갔지?

url에서 page=1로 검색하면 나머지 검색 결과들이 나온다.
이런 검색 결과를 일일이 url에서 증감 해줄 수 없기에 기존에 썼던 Pagination 버튼을 넣고 수정해보자.

2-5. Controller

        ...
        Page<Posts> searchList = postsService.search(keyword, pageable);

        model.addAttribute("searchList", searchList);
        model.addAttribute("keyword", keyword);
        model.addAttribute("previous", pageable.previousOrFirst().getPageNumber());
        model.addAttribute("next", pageable.next().getPageNumber());
        model.addAttribute("hasNext", searchList.hasNext());
        model.addAttribute("hasPrev", searchList.hasPrevious());

        return "posts-search";
    }

페이징 처리 구현 게시글에서 만들었던 index 메소드안의 페이징 구현부분을 가져와 조금 수정했다.
이전/다음 버튼을 눌러 검색결과 페이지를 볼 때 keyword도 같이 넘어가야 되기 때문에 "keyword"를 추가했고,
검색 내용의 이전/다음 페이지 유무 확인을 위해 searchList에 hasNext()와 hasPrevious() 를추가했다.

2-6. posts-search.Mustache

    {{! Page }}
    <div class="pagination justify-content-center">
        {{#hasPrev}}
            <a href="/posts/search?keyword={{keyword}}&page={{previous}}" role="button" class="btn btn-lg bi bi-caret-left-square-fill"></a>
        {{/hasPrev}}
        {{^hasPrev}}
            <a href="/posts/search?keyword={{keyword}}&page={{previous}}" role="button" class="btn btn-lg bi bi-caret-left-square-fill disabled"></a>
        {{/hasPrev}}

        {{#hasNext}}
            <a href="/posts/search?keyword={{keyword}}&page={{next}}" role="button" class="btn btn-lg bi bi-caret-right-square-fill"></a>
        {{/hasNext}}
        {{^hasNext}}
            <a href="/posts/search?keyword={{keyword}}&page={{next}}" role="button" class="btn btn-lg bi bi-caret-right-square-fill disabled"></a>
        {{/hasNext}}
    </div>

a href 부분만 수정했다.

2-7. 최종 결과 확인


검색과 페이징처리까지 모두 완성 했다.

반응형

블로그의 정보

슬기로운 개발생활

coco3o

활동하기