index.html의 pagingReq 함수를 추가한다.
[ index.html ]
<button onclick="saveReq()">글작성</button>
<a href="/board/save">글작성(링크)</a>
<button onclick="listReq()">글목록</button>
<button onclick="pagingReq()">페이징목록</button>
</body>
<script>
// function saveReq() {
//
// }
const saveReq = () => {
location.href = "/board/save";
}
const listReq = () => {
location.href = "/board/";
}
const pagingReq = () => {
location.href = "/board/paging";
}
BoardController에 paging 메서드를 추가한다.
[ BoardController ]
@GetMapping("/paging")
public String paging(@PageableDefault(page = 1) Pageable pageable, Model model) {
// pageable.getPageNumber();
Page<BoardDTO> boardList = boardService.paging(pageable);
int blockLimit = 3;
int startPage = (((int)(Math.ceil((double)pageable.getPageNumber() / blockLimit))) - 1) * blockLimit + 1; // 1 4 7 10 ~~
int endPage = ((startPage + blockLimit - 1) < boardList.getTotalPages()) ? startPage + blockLimit - 1 : boardList.getTotalPages();
// page 갯수 20개
// 현재 사용자가 3페이지
// 1 2 3
// 현재 사용자가 7페이지
// 7 8 9
// 보여지는 페이지 갯수 3개
// 총 페이지 갯수 8개
model.addAttribute("boardList", boardList);
model.addAttribute("startPage", startPage);
model.addAttribute("endPage", endPage);
return "paging";
}
@PageableDefault(page = 1) Pageable pageable
이 어노테이션은 spring data에서 제공하는 페이징 처리를 위한 어노테이션이다.
page 매개변수는 현재 페이지를 나타내며, 기본값은 1이다.
pageable 객체는 페이징 정보를 담고 있다.
Page<BoardDTO> boardList = boardService.paging(pageable);
paging 메서드를 호출하여 현재 페이지에 해당하는 게시글 목록을 가져온다.
이 메서드는 페이징 처리된 결과를 Page 객체로 변환한다.
페이징 처리 및 모델에 속성 추가
페이징 계산: 현재 페이지를 기준으로 시작 페이지와 끝 페이지를 계산한다.
blockLimit 를 이용하여 한 블록에 표시되는 페이지 수를 지정하고,
현재 페이지를 기준으로 시작 페이지와 끝 페이지를 계산한다.
int startPage: 페이징 블록의 시작 페이지를 계산한다.
pageable.getPageNumber(): 현재 페이지의 번호를 가져온다.
(double)pageable.getPageNumber() / blockLimit: 현재 페이지를 블록 단위로 나눈다.
예를 들어, blockLimit가 3이면, 1페이지, 2페이지, 3페이지는 같은 블록에 속한다.
Math.ceil((double)pageable.getPageNumber() / blockLimit)
: 나눈 결과를 올림하여 현재 페이지가 속한 블록의 번호를 얻는다.
-1: 블록 번호에서 1을 때서 시작 페이지가 될 페이지 블록의 첫 번째 페이지를 계산한다.
(예를 들어, 사용자가 3페이지를 요청하면, 내부적으로는 2페이지(0부터 시작하는 인덱스에서 2번째 페이지)에 해당한다.
따라서 블록 번호를 계산할 때 1을 빼서 사용자에게 표시되는 페이지 번호와 내부적인 인덱스를 맞춘다.)
* blockLimit + 1: 블록 내의 첫 번째 페이지 번호를 계산한다.
(예를 들어, 페이지 블록 크기(blockLimit)가 3이고 현재 페이지가 7이라고 가정한다.
블록 번호 계산: 'Math.ceil((double) 7 / 3) - 1' 은 2가 된다. (7이 속한 블록은 3개 페이지로 이루어진 두 번째 블록)
첫 번째 페이지 번호 계산: '(2 * 3) - 1' 은 5가 된다. (현재 블록의 첫 번째 페이지는 5)
int endPage: 페이징 블록의 끝 페이지를 계산한다.
(startPage + blockLimit - 1): 시작 페이지에서 블록 크기를 더하고 1을 빼서 블록 내의 마지막 페이지를 계산한다.
< boardList.getTotalPages()): 계산한 마지막 페이지가 총 페이지 수보다 작을 경우에만 해당 값을 사용한다.
? startPage + blockLimit - 1 : boardList.getTotalPages();: 조건 연산자를 사용하여 마지막 페이지를 정한다.
만약 계산한 마지막 페이지가 총 페이지 수보다 크다면, 총 페이지 수를 사용한다.
(예를 들어, 페이지 블록 크기가 3이고 현재 페이지 블록의 첫 번째 페이지가 4일 때, (4+3-1)은 6이 되어
마지막 페이지가 6까지 표시된다.
그러나 전체 페이지 수가 5라면, 'boardList.getTotalPages()' 값인 5로 대체하여 5까지만 표시하도록 하는 것이다.)
모델에 속성 추가: model.addAttribute 를 사용하여 템블릿(뷰)에서 사용할 속성을 추가한다.
boardList에는 페이징 처리된 게시글 목록이,
startPage와 endPage에는 현재 블록의 시작 페이지와 끝 페이지가 추가된다.
BoardService 클래스에 paging 메서드를 추가한다.
spring data jpa를 사용하여 페이지별로 게시글을 조회하는 메서드이다.
[ BoardService ]
public Page<BoardDTO> paging(Pageable pageable) {
int page = pageable.getPageNumber() - 1;
int pageLimit = 3; // 한 페이지에 보여줄 글 갯수
// 한페이지당 3개씩 글을 보여주고 정렬 기준은 id 기준으로 내림차순 정렬
// page 위치에 있는 값은 0부터 시작
Page<BoardEntity> boardEntities =
boardRepository.findAll(PageRequest.of(page, pageLimit, Sort.by(Sort.Direction.DESC, "id")));
System.out.println("boardEntities.getContent() = " + boardEntities.getContent()); // 요청 페이지에 해당하는 글
System.out.println("boardEntities.getTotalElements() = " + boardEntities.getTotalElements()); // 전체 글갯수
System.out.println("boardEntities.getNumber() = " + boardEntities.getNumber()); // DB로 요청한 페이지 번호
System.out.println("boardEntities.getTotalPages() = " + boardEntities.getTotalPages()); // 전체 페이지 갯수
System.out.println("boardEntities.getSize() = " + boardEntities.getSize()); // 한 페이지에 보여지는 글 갯수
System.out.println("boardEntities.hasPrevious() = " + boardEntities.hasPrevious()); // 이전 페이지 존재 여부
System.out.println("boardEntities.isFirst() = " + boardEntities.isFirst()); // 첫 페이지 여부
System.out.println("boardEntities.isLast() = " + boardEntities.isLast()); // 마지막 페이지 여부
// 목록: id, writer, title, hits, createdTime
Page<BoardDTO> boardDTOS = boardEntities.map(board -> new BoardDTO(board.getId(), board.getBoardWriter(), board.getBoardTitle(), board.getBoardHits(), board.getCreatedTime()));
return boardDTOS;
}
int page = pageable.getPageNumber() - 1;
pageable에서 가져온 현재 페이지 번호를 1 감소시킨다.
spring data jpa에서는 페이지 번호가 0부터 시작하기 때문에,
실제로는 현재 페이지 -1이 실제로 요청할 페이지 번호가 된다.
int pageLimit = 3
한 페이지에 보여줄 글의 개수를 정의한다.
Page<BoardEntity> boardEntities = boardRepository.findAll(PageRequest.of(page, pageLimit, Sort.by(Sort.Direction.DESC, "id")));
spring data jpa의 findAll 메서드를 사용하여 게시글을 페이지별로 조회한다.
현재 페이지, 페이지 크기, id 기준으로 내림차순 정렬하는 조건을 설정한다.
boardEntities.getContent()); // 요청 페이지에 해당하는 글
boardEntities.getTotalElements()); // 전체 글갯수
boardEntities.getNumber()); // DB로 요청한 페이지 번호
boardEntities.getTotalPages()); // 전체 페이지 갯수
boardEntities.getSize()); // 한 페이지에 보여지는 글 갯수
boardEntities.hasPrevious()); // 이전 페이지 존재 여부
boardEntities.isFirst()); // 첫 페이지 여부
boardEntities.isLast()); // 마지막 페이지 여부
Page<BoardDTO> boardDTOS = boardEntities.map
BoardEntity를 BoardDTO로 변환한다.
여기서는 필요한 필드만 선택하여 BoardDTO 객체를 생성한다.
템플릿 패키지에 paging.html 파일을 생성한다.
[ paging.html ]
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button onclick="saveReq()">글작성</button>
<table>
<tr>
<th>id</th>
<th>title</th>
<th>writer</th>
<th>date</th>
<th>hits</th>
</tr>
<tr th:each="board: ${boardList}">
<td th:text="${board.id}"></td>
<td><a th:href="@{|/board/${board.id}|(page=${boardList.number + 1})}" th:text="${board.boardTitle}"></a></td>
<td th:text="${board.boardWriter}"></td>
<td th:text="*{#temporals.format(board.boardCreatedTime, 'yyyy-MM-dd HH:mm:ss')}"></td>
<td th:text="${board.boardHits}"></td>
</tr>
</table>
<!-- 첫번째 페이지로 이동 -->
<!-- /board/paging?page=1 -->
<a th:href="@{/board/paging(page=1)}">First</a>
<!-- 이전 링크 활성화 비활성화 -->
<!-- boardList.getNumber() 사용자:2페이지 getNumber()=1 -->
<a th:href="${boardList.first} ? '#' : @{/board/paging(page=${boardList.number})}">prev</a>
<!-- 페이지 번호 링크(현재 페이지는 숫자만)
for(int page=startPage; page<=endPage; page++)-->
<span th:each="page: ${#numbers.sequence(startPage, endPage)}">
<!-- 현재페이지는 링크 없이 숫자만 -->
<span th:if="${page == boardList.number + 1}" th:text="${page}"></span>
<!-- 현재페이지 번호가 아닌 다른 페이지번호에는 링크를 보여줌 -->
<span th:unless="${page == boardList.number + 1}">
<a th:href="@{/board/paging(page=${page})}" th:text="${page}"></a>
</span>
</span>
<!-- 다음 링크 활성화 비활성화
사용자: 2페이지, getNumber: 1, 3페이지-->
<a th:href="${boardList.last} ? '#' : @{/board/paging(page=${boardList.number + 2})}">next</a>
<!-- 마지막 페이지로 이동 -->
<a th:href="@{/board/paging(page=${boardList.totalPages})}">Last</a>
</body>
<script>
const saveReq = () => {
location.href = "/board/save";
}
</script>
</html>
<a th:href="${boardList.first} ? '#' : @{/board/paging(page=${boardList.number})}">prev</a>
${boardList.first}
boardList는 페이지네이션에 사용되는 spring data의 'Page' 객체이다.
'first' 속성은 현재 페이지가 첫 페이지인지 여부를 나타내는 속성이다.
만약 현재 페이지가 첫 페이지이면 'true'를, 그렇지 않으면 'false'를 반환한다.
? '#' : @{/board/paging(page=${boardList.number})}
삼항 연산자를 사용하여 현재 페이지가 첫 페이지면 '#' (링크가 비활성화됨)를,
그렇지 않으면 다음과 같은 페이지로 이동하는 링크를 생성한다.
<span th:each="page: ${#numbers.sequence(startPage, endPage)}">
타임리프의 반복문 사용하여 startPage부터 endPage까지의 숫자 시퀀스를 생성한다.
이는 페이지 블록에 표시할 번호들을 나타낸다.
<span th:if="${page == boardList.number + 1}" th:text="${page}"></span>
현재 페이지 번호에 해당하는 경우, 링크 없이 숫자만을 출력한다.
타임리프의 조건문을 사용하여 현재 페이지에 해당하는 경우에만 숫자를 표시한다.
<span th:unless="${page == boardList.number + 1}">
현재 페이지 번호가 아닌 경우, 링크를 생성한다.
th:unless는 주어진 조건이 거짓인 경우에만 내용을 처리한다.
따라서 현재 페이지 번호와 다른 페이지 번호에 대해 링크를 생성하게 된다.
<a th:href="@{/board/paging(page=${page})}" th:text="${page}"></a>
링크의 URL은 /board/paging 이고, 페이지 번호는 page 변수로 동적으로 지정된다.
또한, 링크의 텍스트로는 해당 페이지 번호가 표시된다.
실행 ㄱㄱㄱㄱㄱ
http://localhost:8092/board/paging?page=1
http://localhost:8092/board/paging?page=4
'Spring Boot' 카테고리의 다른 글
[게시판 만들기 (10)] 파일 첨부_다중파일 첨부 (0) | 2024.01.15 |
---|---|
[게시판 만들기 (9)] 게시글 페이징_페이징 후 상세조회 (0) | 2024.01.14 |
[게시판 만들기 (7)] 게시글 삭제 (0) | 2024.01.14 |
[게시판 만들기 (6)] 게시글 수정 (0) | 2024.01.14 |
[게시판 만들기 (5)] 게시글 조회 (0) | 2024.01.14 |