안녕하세요 코이킹입니다.
이번 포스트는 페이징 처리 기능 구현에 대한 내용이 되겠습니다.
1. 목표
- 스프링 부트를 사용해서 DB의 데이터를 웹에 표시할 때 페이징 처리를 할 수 있다.
2. 페이징 처리란?
하나의 페이지에 표시해야 할 데이터가 너무 많을 경우, 데이터를 나누어 표시하는 것이 페이징 처리입니다.
페이징 처리를 구현하기 위해선 다음과 같은 값들을 가지고 다양한 처리를 해주어야 할 필요가 있습니다.
- 전체 데이터의 수
- 하나의 페이지에 표시할 데이터의 수
- 전체 페이지의 수
- 현재의 페이지 번호
- 하나의 페이지에 표시할 데이터는 몇 번부터 몇 번까지 인지 계산하기
따라서 페이징 처리를 직접 구현하려하면 상당히 어렵습니다만, 스프링 부트가 제공하는 기능을 활용하면 페이징 처리를 쉽게 구현할 수 있습니다.
3. 페이징 처리 구현 소스코드와 해석
1) 글목록 템플릿 : /template-springboot/src/main/resources/templates/board/list.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, inital-scale=1.0"/>
</head>
<body>
<div>
<div class="nav justify-content-end px-5 pt-2">
<button class="btn btn-info text-white" id="write-board" type="button" onclick="location.href='/board/write'">
Contents Write
</button>
</div>
<hr/>
<div>
<table class="table" border="1">
<thead class="table-dark">
<tr>
<th>No</th>
<th>Contents Type</th>
<th>Title</th>
<th>CreatedTime</th>
<th>UpdatedTime</th>
<th>Likes</th>
</tr>
</thead>
<tbody>
<tr th:each="board: ${pageInfo.getContent()}">
<td th:text="${board.boardNo}"></td>
<td th:text="${board.type}==1 ? 'Normal' : 'MemberShip'"></td>
<td><a th:href="@{/board/read/{boardNo}(boardNo=${board.boardNo})}">[[${board.title}]]</a></td>
<td th:text="${board.createdTime}"></td>
<td th:text="${board.updatedTime}"></td>
<td th:text="${board.likes}"></td>
</tr>
</tbody>
</table>
</div>
<hr/>
<!-- paging -->
<div id="paging-container" class="pagination nav justify-content-center px-4">
<div th:class="${pageInfo.first} ? 'page-item disabled' : 'page-item'">
<span class="page-link" th:if="${pageInfo.first}">Prev</span>
<a class="page-link" th:if="${not pageInfo.first}" th:href="@{/board/list(page=1)}">Prev</a>
</div>
<div th:class="${p} != ${pageInfo.number+1} ? 'page-item' : 'page-item disabled'" th:each="p : ${#numbers.sequence(1, (pageInfo.totalPages))}">
<span class="page-link" th:if="${p} == ${pageInfo.number+1}" th:text="'['+${p}+'] '"></span>
<a class="page-link" th:if="${p} != ${pageInfo.number+1}" th:href="@{/board/list(page=${p})}">
<span th:text="'['+${p}+']'"></span>
</a>
</div>
<div th:class="${pageInfo.last} ? 'page-item disabled' : 'page-item' ">
<span class="page-link" th:if="${pageInfo.last}">Next</span>
<a class="page-link" th:if="${not pageInfo.last}" th:href="@{/board/list(page=(${pageInfo.totalPages}))}">Next</a>
</div>
</div>
</div>
</body>
</html>
- 45~48행 : 다음 페이지 버튼을 조건부로 활성화한다.
- 49~54행 : 페이지 번호를 표시한다. 현재 페이지의 번호의 경우 링크를 비활성화한다.
- 55~58행 : 다음 페이지 버튼을 조건부로 활성화 한다.
2) 컨트롤러 :
package com.sb.template.controller;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import com.sb.template.entity.Board;
import com.sb.template.enums.BoardType;
import com.sb.template.exception.InvalidParamException;
import com.sb.template.exception.ProcFailureException;
import com.sb.template.forms.BoardForm;
import com.sb.template.service.BoardService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
@RequestMapping(path = "/board")
public class BoardController {
@Autowired
private BoardService boardService;
@RequestMapping(method = RequestMethod.GET, value = {"list", "/", ""})
public String viewBoardList(Pageable pageable, Model model) {
Page<Board> pageInfo = boardService.getAllBoard(pageable);
model.addAttribute("pageInfo", pageInfo);
return "board/list";
}
...
...
...
}
- 41행 : Pageable을 변수로 매개변수로 추가한다. 요청 파라미터로 page(페이지 번호), size(한 페이지에 보여줄 데이터 수)를 보내면 알아서 Pageable에 매핑된다.
- 43~44행 : 게시글 데이터의 리스트가 아닌 Page객체를 리턴 받도록 수정.
3) 서비스 : /template-springboot/src/main/java/com/sb/template/service/BoardService.java
package com.sb.template.service;
import java.util.NoSuchElementException;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import com.sb.template.annotation.Timer;
import com.sb.template.entity.Board;
import com.sb.template.enums.BoardType;
import com.sb.template.repo.BoardRepository;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class BoardService {
@Autowired
private BoardRepository boardRepository;
@Timer
public Page<Board> getAllBoard(Pageable pageable) {
int page = (pageable.getPageNumber() == 0) ? 0 : (pageable.getPageNumber() - 1);
pageable = PageRequest.of(page, pageable.getPageSize());
Page<Board> res = boardRepository.findAll(pageable);
if (res == null) return null;
log.debug("Data from DB : {}", res);
return res;
}
...
...
...
}
- 29~30 행 : Pageable을 매개변수로 리포지토리의 findAll메서드를 호출하면, 페이징 된 데이터를 DB로부터 받아올 수 있다.
4. 동작확인
5. 전체 소스코드
https://github.com/leeyoungseung/template-springboot/tree/feature/07_paging
이것으로 페이징 처리 구현에 대한 포스트는 마치겠습니다.
다음 포스트는 레이아웃 합치기와 부트스트랩(CSS) 적용에 대한 내용이 되겠습니다.
'프로그래밍 > Springboot-토이프로젝트' 카테고리의 다른 글
【게시판-09】회원가입 (1) | 2022.09.20 |
---|---|
【게시판-08】타임리프 템플릿 결합과 부트스트랩 적용 (0) | 2022.09.17 |
【게시판-번외06】예외처리 (0) | 2022.09.16 |
【게시판-번외05】유효성 검사 (0) | 2022.09.14 |
【게시판-번외04】AOP를 적용한 로그출력 (0) | 2022.09.14 |
댓글