Spring Boot 게시판 만들기 11 - 페이징 처리하기

11. 페이징 처리하기

메인 게시판을 접근하게 되면 한번에 너무 많은 게시글들이 쏟아져 나오게 돼 보기가 좋지 않고 서버도 많은 양의 데이터를 한번에 보내야 하기 때문에 성능에서도 좋지 않다.
사용자에게 전체 데이터에서 적당한 양의 데이터만 보여줘 사용자 입장에서도 서버 입장에서도 부담이 없게 한다.

Control 로직 수정하기

Spring에서 제공하는 Pageable 인터페이스를 사용하면 쉽게 페이징 기능을 사용할 수 있다.

BoardController.java

@Controller
@RequiredArgsConstructor
public class BoardController {
private final PostService postService;

// @GetMapping("/")
// public String board(Model model){
// List<Post> posts = postService.getAllPosts();
// model.addAttribute("PostList", posts);
// return "board";
// }

@GetMapping("/")
public String board(@PageableDefault Pageable pageable, Model model) {
Page<Post> posts = postService.getPosts(pageable);
model.addAttribute("PostList", posts);
return "board";
}
}

테스트 코드 로직 변경

@Test
@DisplayName("모든 Post를 가져온다.")
public void board() throws Exception {
List<Post> posts = new ArrayList<>();
posts.add(Post.builder()
.id(1L)
.title("test")
.name("tester")
.content("test")
.writeTime(LocalDateTime.now())
.build()
);

Page<Post> pagePosts = new PageImpl<>(posts);

PageRequest pageRequest = PageRequest.of(1, 10);

given(postService.getPosts(pageRequest)).willReturn(pagePosts);
ResultActions resultActions = mockMvc.perform(get("/")
.param("page", "1")
// .flashAttr("PostList", new ArrayList<>())
);

resultActions
.andExpect(status().isOk())
.andDo(print());
verify(postService).getPosts(pageRequest);
}

Servic 로직 추가

PageRequestPageable 인터페이스를 구현해 놓은 클래스이다. page, size, sort 를 변수를 설정함으로써 쉽게 paging 요청을 할 수 있다.

page : 찾을 페이지
size : 한 페이지 내에 가져올 목록(데이터)의 개수
sort : 페이징을 정렬하는 방식

Paging은 sort를 통해 데이터들을 정렬하고 size를 통해 데이터들을 그룹으로 나눈 후 각 그룹에 번호를 붙이고 page를 통해 해당 그룹을 가져온다.

PostService.java

public Page<Post> getPosts(Pageable pageable) {
int page;
if(pageable.getPageNumber() <= 0){
page = 0;
}else{
page = pageable.getPageNumber()-1;
}

Pageable requestPageable = PageRequest.of(page, pageable.getPageSize());
return postRepository.findAll(requestPageable);
}

Id 역순으로 정렬하기

@Transactional
public void deletePostById(Long id){
postRepository.deleteById(id);
}

public Page<Post> getPosts(Pageable pageable) {
int page;
if(pageable.getPageNumber() <= 0){
page = 0;
}else{
page = pageable.getPageNumber()-1;
}

Pageable requestPageable = PageRequest.of(page, pageable.getPageSize(), Sort.by(Sort.Direction.DESC, "id"));
return postRepository.findAll(requestPageable);
}

타임리프에 paging관련 설정을 해주기

Thymeleaf의 th:with 속성을 사용하면 지역변수를 사용할 수 있다.

  • startIdx=${T(Math).floor(PostList.number/5)}*5+1 : pagenation에서의 시작 번호
  • lastIdx=(${PostList.totalPages} > ${startIdx}+4) ? ${startIdx}+4 : ${PostList.totalPages} : pagenation에서의 마지막 번호
<ul class="pagination" style="justify-content: center;"
th:with="startIdx=${T(Math).floor(PostList.number/5)}*5+1,
lastIdx=(${PostList.totalPages} > ${startIdx}+4) ? ${startIdx}+4 : ${PostList.totalPages}"
>
<li th:class="${PostList.first} ? 'disabled'">
<a class="page-link" th:href="${PostList.first} ? '#' : @{/(page=${startIdx}-1)}"
aria-label="Previous">&laquo;</a>
</li>

<li th:style="${PostList.first}">
<a class="page-link" th:href="@{/(page=${PostList.number})}">&lsaquo;</a>
</li>

<li th:class="(${page} == ${PostList.number}+1)"
th:each="page: ${#numbers.sequence(startIdx, lastIdx)}">
<a class="page-link" th:text="${page}" th:href="@{/(page=${page})}"></a>
</li>

<li th:style="${PostList.last}">
<a class="page-link" th:href="@{/(page=${PostList.number}+2)}">&rsaquo;</a>
</li>

<li th:class="${PostList.last} ? 'disabled'">
<a class="page-link" th:href="${PostList.last} ? '#' : @{/(page=${lastIdx}+1)}"
aria-label="Next">&raquo;</a>
</li>
</ul>

이전 그룹으로 이동하기

  • PostList.first : 현재 페이지가 제일 첫페인지 아닌지 (boolean)
  • PostList.last : 현재 페이지가 제일 마지막 페이지 인지 아닌지 (boolean)
<li th:class="${PostList.first} ? 'disabled'">
<a class="page-link" th:href="${PostList.first} ? '#' : @{/(page=${startIdx}-1)}"
aria-label="Previous">&laquo;</a>
</li>

이전 페이지로 이동하기

<li th:style="${PostList.first}">
<a class="page-link" th:href="@{/(page=${PostList.number})}">&lsaquo;</a>
</li>

페이지 그룹 보여주기

<li th:class="(${page} == ${PostList.number}+1)"
th:each="page: ${#numbers.sequence(startIdx, lastIdx)}">
<a class="page-link" th:text="${page}" th:href="@{/(page=${page})}"></a>
</li>

다음 페이지로 이동하기

<li th:style="${PostList.last}">
<a class="page-link" th:href="@{/(page=${PostList.number}+2)}">&rsaquo;</a>
</li>

다음 그룹으로 이동하기

<li th:class="${PostList.last} ? 'disabled'">
<a class="page-link" th:href="${PostList.last} ? '#' : @{/(page=${lastIdx}+1)}"
aria-label="Next">&raquo;</a>
</li>

List 번호 수정하기

페이지를 이동해도 List번호가 1~10 으로 밖에 표현되지 않았다. 각페이지를 이동할 때 페이지 번호에 각 세부 번호를 나타낼 수 있도록 표현을 바꿔준다.

<tbody class="text-center">
<tr th:each="Post:${PostList}" th:id="*{Post.id}">
<td class="align-middle" th:text="${PostStat.index + 1} + ${PostList.number}*10"></td>
<td class="align-middle">
<a th:href="@{/post/{id}(id=${Post.id})}" th:text="${Post.title}"></a>
</td>
<td class="align-middle" th:text="${Post.name}"></td>
<td class="align-middle" th:text="${Post.writeTime}"></td>
<td class="text-center align-middle">
<a class="btn btn-primary" th:href="@{/post/{id}/revise(id=${Post.id})}">수정</a>
<a href="#" th:href="'javascript:deletePost('+${Post.id}+')'"
class="btn btn-danger">삭제</a>
<!-- <button id="delete-btn" type="submit" class="btn btn-danger" th:onclick="deletePost([[ ${Post.id} ]]);">삭제</button>-->
</td>
</tr>
</tbody>
Share