전체 페이지별로 게시물들을 보여주는 것 외에 사용자가 필요한 정보를 찾기 위핸 검색 기능을 추가해보려고 한다.
검색한 데이터들도 저번 포스트에 적용했던 페이징을 적용한다.
Srping Data JpaRepository 쿼리
간단한 쿼리문들은 JpaRepository에서 메서드를 정의해주는 것으로 대체할 수 있다.
findBy 뒤에 엔티티의 컬럼과 Containing, Contains, IsContaining 등과 조합한 메소드에 파라미터로 컬럼을 넘겨주면 DB로 쿼리문을 날릴 수 있고, 이전 페이징 처리와 마찬가지로 JPARepository를 사용하면 Page 객체로 리턴 받을 수 있다.
PostRepository
public interface PostRepository extends JpaRepository<Post, Long> {
Page<Post> findByTitleContainingOrContentContaining(String title, String content, Pageable pageable);
}
PostService
@Service
@RequiredArgsConstructor
public class PostService {
private final PostRepository postRepository;
private final UserRepository userRepository;
public Page<PostResponse> search(String searchText, Pageable pageable) {
return postRepository.findByTitleContainingOrContentContaining(searchText, searchText, pageable)
.map(PostResponse::new);
}
}
페이징 처리와 마찬가지로 컨트롤러에서 검색할 단어와 Pageable를 넘겨받고 Repository에서 받은 Page를 Page로 바꿔서 리턴해준다.
PostController
@Controller
@RequiredArgsConstructor
@RequestMapping("/board")
public class PostController {
private final PostService postService;
@GetMapping("/search")
public String search(@PageableDefault(size = 2, sort = "id", direction = Sort.Direction.DESC) Pageable pageable,
String searchText, Model model) {
Page<PostResponse> searchPosts = postService.search(searchText, pageable);
PageDto searchPageResponse = PageDto.of(searchPosts);
model.addAttribute("posts", searchPageResponse);
model.addAttribute("searchText", searchText);
return "post/postSearch";
}
}
View 단에서 사용할 PageDto와 페이지를 움직여도 searchText롤 계속 유지해야 전체 게시물 페이지로 이동하지 않기 때문에 model에 searchText를 함께 넘겨준다.
Thymeleaf Search View
<main class="flex-shrink-0">
<div class="container">
<h2 class="mt-5">게시판</h2>
<div>총 건수 : <span th:text="${posts.contents.totalElements}"></span></div>
<form class="form-inline d-flex justify-content-end" method="get" th:action="@{/board/search/}" th:value="${param.searchText}">
<div class="form-group mx-sm-3 mb-2">
<label for="searchText" class="sr-only"></label>
<input type="text" class="form-control" id="searchText" name="searchText">
</div>
<button type="submit" class="btn btn-light mb-2">search</button>
</form>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">글번호</th>
<th scope="col">제목</th>
<th scope="col">작성자</th>
</tr>
</thead>
<tbody>
<tr th:each="post : ${posts.contents}">
<th th:text="${post.id}">1</th>
<td><a th:text="${post.title}" th:href="@{|/board/posts/${post.id}|}">Mark</a></td>
<td th:text="${post.user.username}">작성자 들어갈 곳</td>
</tr>
</tbody>
</table>
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center">
<li class="page-item" th:classappend="${1==posts.nowPage} ? 'disabled'">
<a class="page-link" th:href="@{/board/search(page=${posts.nowPage -2}, searchText=${searchText})}">Previous</a>
</li>
<li class="page-item" th:classappend="${i==posts.nowPage} ? 'disabled'"
th:each="i : ${#numbers.sequence(posts.startPage, posts.endPage)}">
<a class="page-link" th:href="@{/board/search(page=${i - 1}, searchText=${searchText})}" th:text="${i}">1</a></li>
<li class="page-item" th:classappend="${posts.totalPages==posts.nowPage} ? 'disabled'">
<a class="page-link" href="#" th:href="@{/board/search(page=${posts.nowPage}, searchText=${searchText})}">Next</a>
</li>
</ul>
</nav>
<div class="text-end">
<a type="button" class="btn btn-primary" th:href="@{/board/write}">Write</a>
</div>
</div>
</main>
전체 게시물 페이지에서 총 게시물 수, 보여지는 게시물, 페이징 처리 모두 검색 결과를 바탕으로 보여줘야하므로 걸려있는 링크들을 전체 페이지에서 검색 페이지로 돌려주고 컨트롤러에서 받은 searchText를 활용해 검색 페이징 처리를 했다.
결과
전체 게시물 중에서 검색 조건에 해당하는 게시물 수와 그에 해당하는 페이징 처리, 검색창이 유지된다.
개선
어떤 검색조건을 갖고 있는지 조금 더 명확하게 보여줘야할 거 같고, 게시물이 없을 때 보여줄 View 페이지와 컨트롤러가 추가해야 할 것 같다.