앞선 포스팅에서 게시글 페이징 기능을 구현 했다.
https://notorious.tistory.com/340
[게시판 서비스] 게시글 페이징 처리 구현
게시판 서비스에 작성된 게시글을 페이징 처리 하는 것을 구현 해야 한다. ※ 조건 ※ 1. 게시글의 'id' 를 기준으로 내림차순 정렬2. '이전' '1' '2' '3' '4' '5' '다음' 과 같이 페이지 번호 구현 1. Po
notorious.tistory.com
이번에는 게시판 서비스에서 빠질 수 없는 '검색 기능' 과 '정렬' 기능을 페이징과 함께 구현 해야 한다.
※ 조건 ※
1. 정렬 조건 : '조회수' , '생성일자' 기준으로 내림차순 정렬
2. 검색 조건 : 검색 후 검색 조건에 부합하는 게시글들 역시 페이징 되어야 한다.
1. PostRepository
public interface PostRepository extends JpaRepository<Post, Long> {
// Post 전체 검색 (페이징)
Page<Post> findAll(Pageable pageable);
// Post Title 검색 (페이징)
Page<Post> findByTitleContaining(String searchKeyword, Pageable pageable);
}
게시글 제목 검색을 위한 'findByTitleContaining' 메서드를 추가한다.
반환 타입은 'Page<Post>' 이고 파라미터로 검색 키워드(searchKeyword) 와 페이징 조건(pageable) 을 갖는다.
2. PostService
// PostService.class 中 ...
/**
* 게시글 목록
*/
public Page<Post> pageList(Pageable pageable) {
return postRepository.findAll(pageable);
}
/**
* 게시글 제목 검색
*/
public Page<Post> search(String searchKeyword, Pageable pageable) {
return postRepository.findByTitleContaining(searchKeyword, pageable);
}
기존 페이징 처리된 게시글 목록 로직 - pageList
페이징 처리된 게시글 검색 목록 로직 - search
3. Controller
'정렬' 관련 로직은 Controller 단에서 추가한다.
//게시글 페이징 테스트
@GetMapping("/paging-test")
public String paging_test(@PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable,
@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "sort", required = false) String sort,
Model model) {
//정렬 조건 설정
if ("views".equals(sort)) {
pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(Sort.Direction.DESC, "view"));
} else if ("createdAt".equals(sort)) {
pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(Sort.Direction.DESC, "createdAt"));
}
//검색 조건 설정
Page<Post> list = (keyword != null && !keyword.isEmpty())
? postService.search(keyword, pageable)
: postService.pageList(pageable);
Page<PostResponseDto> posts = list.map(PostResponseDto::new);
model.addAttribute("posts", posts); //응답 DTO-PAGE
model.addAttribute("sort", sort); //정렬 방식
model.addAttribute("keyword", keyword); //검색 키워드
model.addAttribute("previous", pageable.previousOrFirst().getPageNumber()); //이전 페이지 정보
model.addAttribute("next", pageable.next().getPageNumber()); //다음 페이지 정보
model.addAttribute("hasPrevious", list.hasPrevious()); //이전 페이지 존재 여부
model.addAttribute("hasNext", list.hasNext()); //다음 페이지 존재 여부
/** 페이지 블록 계산
* currentPage = 5
* User : 5 , Spring : 4
* startPage = 1
* endPage = 5
* <= 1 2 3 4 5 =>
*/
int currentPage = pageable.getPageNumber() + 1; //현재 페이지 정보(User side)
model.addAttribute("current", currentPage);
int blockSize = 5;
int startPage = ((currentPage - 1) / blockSize) * blockSize + 1; //블럭 시작 페이지
int endPage = Math.min(startPage + blockSize - 1, list.getTotalPages()); //블럭 마지막 페이지
model.addAttribute("startPage", startPage);
model.addAttribute("endPage", endPage);
return "test/paging-test";
}
정렬 기능

파라미터로 정렬 기준을 의미하는 'sort' 를 받는다.
'sort' 변수가 'views' 라면, 조회수 기준 내림차순 정렬을 한다.
'sort' 변수가 'createdAt' 라면, 작성일자 기준 내림차순 정렬을 한다.
※ pageable 을 통해, 정렬 방식을 설정한다.
검색 기능

파라미터로 검색 키워드를 의미하는 'keyword' 를 받는다.
삼항 연산자를 이용해
'keyword' 가 공백이 아닌 값이 있다면, 'postService.search' 를 이용하여 페이징 처리된 검색 결과를 list로 하고,
'keyword' 가 공백이 이거나 값이 없다면, 'postService.pageList' 를 이용하여 페이징 처리된 게시글 목록 list로 한다.

'정렬 방식' 과 '검색 키워드' 를 model 에 실어서 VIEW 로 보낸다.
4. VIEW
0. 검색 창 + 검색 버튼 (Header에 위치)
<!--검색 폼-->
<form th:action="@{/}" method="get" class="d-flex" role="search">
<input class="form-control me-2" type="search" placeholder="검색" name="keyword">
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
1. 게시글 목록
<!--게시글 목록-->
<table class="table table-striped">
<thead>
<tr class="table-dark">
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>
<a th:href="@{/paging-test(keyword=${keyword}, sort='views', page=${current - 1})}">조회수</a>
</th>
<th>
<a th:href="@{/paging-test(keyword=${keyword}, sort='createdAt', page=${current - 1})}">작성일자</a>
</th>
</tr>
</thead>
<tbody>
<tr th:each="post : ${posts}">
<td th:text="${post.id}">1</td>
<td>
<a th:href="@{|/post/${post.id}|}" th:text="${post.title}">제목</a>
</td>
<td th:text="${post.memberNickname}">작성자</td>
<td th:text="${post.view}">조회수</td>
<td th:text="${post.createdAt}">작성일자</td>
</tr>
</tbody>
</table>
정렬 기능을 위해, '조회수' 와 '작성일자' 컬럼에 <a> 태그를 이용해 URL 파라미터에 'sort' 를 설정한다.
URL 파라미터에 'sort' , 'keyword', 'page' 를 모두 추가한다.
2. 페이지네이션
<ul class="pagination">
<li class="page-item">
<a th:if="${hasPrevious}" th:href="@{/paging-test(keyword=${keyword}, sort=${sort} ,page=${previous})}"
role="button" class="page-link">이전</a>
<a th:if="${!hasPrevious}" th:href="@{/paging-test(keyword=${keyword}, sort=${sort} ,page=${previous})}"
role="button" class="page-link disabled">이전</a>
</li>
<li class="page-item" th:each="pageNum : ${#numbers.sequence(startPage, endPage)}"
th:classappend="${pageNum == current} ? active : ''">
<a th:href="@{/paging-test(keyword=${keyword}, sort=${sort} ,page=${pageNum - 1})}" th:text="${pageNum}"
role="button" class="page-link">페이지 번호</a>
</li>
<li class="page-item">
<a th:if="${hasNext}" th:href="@{/paging-test(keyword=${keyword}, sort=${sort} ,page=${next})}"
role="button" class="page-link">다음</a>
<a th:if="${!hasNext}" th:href="@{/paging-test(keyword=${keyword}, sort=${sort} ,page=${next})}"
role="button" class="page-link disabled">다음</a>
</li>
</ul>
URL 파라미터에 'sort' , 'keyword', 'page' 를 모두 추가한다.
완성화면

정렬 + 페이징 + 검색 모두 정상적으로 작동하는 것을 확인 할 수 있다.