일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Checked 의사 클래스
- html 태그
- sub태그
- go live
- sup태그
- 자식결합자
- iframe 태그
- 임베디드
- iframe
- 전체 선택자
- id 선택자
- html tag i
- RGB
- padding 속성
- RGBA
- width속성
- html
- 인접 형제 결합자
- not 의사클래스
- br 태그
- i 태그
- Live Server
- tag html
- reveal in file explorer
- 아두이노
- background-color 속성
- 일반 형제 결합자
- focus 의사클래스
- css
- height속성
- Today
- Total
so woon!
페이징 구현 본문
학습일 : 2022. 11. 30
페이징을 위해 base에 models라는 디렉토리를 하나 새로 생성한 뒤
PagingModel 이라는 이름의 클래스를 생성
PagingModel.java
<36 페이지 요청시> 31------ 36 ------- 40 표시 s p e |
이런식으로 나오게 구현을 해야 하기 때문에
앞서 선생님이랑 같이 풀어본 페이징 관련 공식들인
페이징(Paging) 혹은 페지네이션(Pagination) | ||
[ c ] 페이지 당 표시할 게시글의 수 | 10 | |
[ p ] 요청한 페이지 번호 | Assert >= 1 | |
[ t ] 전체 게시글의 개수 | DB가 | |
[ n ] 이동 가능한 최소 페이지 | 1 | |
[ x ] 이동 가능한 최대 페이지 | t / c + ( t % c == 0 ? 0 : 1) | |
(t -1) / c + 1 | 성능에 유리 ===> 이걸 사용하도록 | |
[ s ] 표시 시작 페이지 | ( ( p - 1 ) / 10 ) * 10 + 1 ) | |
[ e ] 표시 끝 페이지 (이동가능한 최대 페이지 이하여야 함) |
Math.min( s + 9 , x ) | ===> 이걸 사용하도록 |
s + 9 > x ? x : ( s + 9 ) |
이 공식들을 클래스에 적어주도록 한다.
먼저, (alt+insert) 하여 constructor 를 추가 하는데,
하나는 totalCount, requestPage 를,
나머지 하나는 countPerPage, totalCount, requestpage 총 3개를 선택하고 constructor추가를 해준다.
그러고 나서 아래와 같이 공식을 적어준다.
package dev.xowoony.studymemberbbs.models;
//페이징
public class PagingModel {
public final int countPerPage; // 페이지당 표시할 게시글의 개수
public final int totalCount; // 전체 게시글 개수
public final int requestPage; // 요청한 페이지 번호
public final int maxPage; // 이동 가능한 최대 페이지
public final int minPage; // 이동 가능한 최소 페이지
public final int startPage; // 표시 시작 페이지
public final int endPage; // 표시 끝 페이지
public PagingModel(int totalCount, int requestPage) {
this(10, totalCount, requestPage);
}
public PagingModel(int countPerPage, int totalCount, int requestPage) {
this.countPerPage = countPerPage;
this.totalCount = totalCount;
this.requestPage = requestPage;
this.minPage = 1;
this.maxPage = (totalCount - 1) / countPerPage + 1;
this.startPage = ((requestPage - 1) / 10) * 10 + 1;
this.endPage = Math.min(startPage + 9, maxPage);
}
}
list.html
<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title th:text="${'또치뱅크 - 게시판 목록 조회'}"></title>
<th:block th:replace="~{fragments/head :: common}"></th:block>
<link rel="stylesheet" th:href="@{/bbs/resources/stylesheets/list.css}">
<script th:if="${board == null}">
alert('존재하지 않는 게시판입니다.');
if (window.history.length > 1) {
window.history.back();
} else {
window.close();
}
</script>
<script defer th:src="@{/resources/libraries/ckeditor/ckeditor.js}"></script>
<script defer th:src="@{/bbs/resources/scripts/list.js}"></script>
</head>
<body th:if="${board != null}">
<th:block th:replace="~{fragments/body :: header}"></th:block>
<th:block th:replace="~{fragments/body :: cover}"></th:block>
<main class="--main main">
<div class="--content content">
<h1 class="title">게시판</h1>
<table class="table" id="table">
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>조회수</th>
<th>일시</th>
<th>추천수</th>
</tr>
</thead>
<tbody>
<tr th:each="article : ${articles}">
<!--번호-->
<td th:text="${article.getIndex()}"></td>
<td>
<a class="title" th:href="@{/bbs/read (aid=${article.getIndex()})}">
<!--글번호-->
<span class="text" th:text="${article.getTitle()}"></span>
<!--글 타이틀-->
<span class="comment" th:text="${'[' + article.getCommentCount() + ']'}"></span>
<!--댓글 달린 갯수 표시-->
</a>
</td>
<td th:text="${article.getUserNickname()}"></td>
<!-- 작성자 -->
<td th:text="${article.getView()}"></td>
<!-- 조회수 -->
<td th:text="${#dates.format(article.getModifiedOn(), 'yyyy-MM-dd-HH:mm:ss')}"></td>
<!-- 일시 -->
<td th:text="${article.getView()}"></td>
<!-- 추천수 -->
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="6">
<div class="page-container"
th:with="urlBuilder = ${T(org.springframework.web.servlet.support.ServletUriComponentsBuilder).fromCurrentRequest()}">
<!-- 페이징-->
<!-- << -->
<a class="page"
th:href="@{${urlBuilder.replaceQueryParam('page', paging.minPage).build().toUriString()}}"
th:if="${paging.requestPage > 1}">
<i class="fa-solid fa-angles-left"></i>
</a>
<!-- < -->
<a class="page"
th:href="@{${urlBuilder.replaceQueryParam('page', paging.requestPage -1).build().toUriString()}}"
th:if="${paging.requestPage > 1}">
<i class="fa-solid fa-angle-left"></i>
</a>
<!-- 얘가 기준 - 요청한 페이지 -->
<a th:each="page : ${#numbers.sequence(paging.startPage, paging.endPage)}"
th:class="${'page ' + (page == paging.requestPage ? 'selected' : '')}"
th:text="${page}"
th:href="@{${urlBuilder.replaceQueryParam('page', page).build().toUriString()}}"></a>
<!-- 얘가 기준 - 요청한 페이지-->
<!-- > -->
<a class="page"
th:href="@{${urlBuilder.replaceQueryParam('page', paging.requestPage + 1).build().toUriString()}}"
th:if="${paging.requestPage < paging.maxPage}">
<i class="fa-solid fa-angle-right"></i>
</a>
<!-- >> -->
<a class="page"
th:href="@{${urlBuilder.replaceQueryParam('page', paging.maxPage).build().toUriString()}}"
th:if="${paging.requestPage < paging.maxPage}">
<i class="fa-solid fa-angles-right"></i>
</a>
</div>
</td>
</tr>
</tfoot>
</table>
<!-- 글쓰기 버튼-->
<div class="button-container" id="buttonContainer">
<a class="--object-button" th:href="@{/bbs/write (bid=${board.getId()})}">글쓰기</a>
</div>
<form class="search-form" id="searchForm" method="get">
<label class="label">
<span hidden>검색 기준</span>
<select class="--object-input" name="criterion">
<option value="all" th:selected="${criterion == null || criterion.equals('all')}">제목 + 내용</option>
<option value="title" th:selected="${criterion != null && criterion.equals('title')}">제목</option>
<option value="nickname" th:selected="${criterion != null && criterion.equals('nickname')}">작성자
</option>
</select>
</label>
<label class="label">
<span hidden>검색어</span>
<input class="--object-input" maxlength="50" name="keyword" placeholder="검색어를 입력해 주세요." type="text"
th:value="${#request.getParameter('keyword')}">
</label>
<input name="bid" type="hidden" th:value="${board.getId()}">
<input class="--object-button" type="submit" value="검색">
</form>
</div>
</main>
<input type="hidden" th:value="${bid}" name="bid">
<th:block th:replace="~{fragments/body :: footer}"></th:block>
</body>
</html>
BbsMapper.xml
SELECT 쿼리를 이용한다.
resultType은 int 이다.
1. 만약 criterion이 null이 아니고 criterion이 nickname일 경우
`articles`.`user_email` 과 `user`.`email` 같을 경우에
`study_member`.`users` 테이블을 LEFT JOIN 한다.
2. 만약 criterion 이 null 이 아니고 criterion이 title일 경우
제목에 띄어쓰기가 있으면
띄어쓰기가 없는 것으로 바꾸어 준다.
3. 만약 criterion이 null 이 아니고 criterion이 all (제목+내용) 일 경우
제목이나 내용에 띄어쓰기가 포함되어 있으면
띄어쓰기가 없는 것으로 바꾸어 주도록 한다.
<!-- 목록 -->
<select id="selectArticlesByBoardId"
resultType="dev.xowoony.studymemberbbs.vos.bbs.ArticleReadVo">
SELECT `article`.`index` AS `index`,
`article`.`user_email` AS `userEmail`,
`article`.`board_id` AS `boardId`,
`article`.`title` AS `title`,
`article`.`view` AS `view`,
`article`.`written_on` AS `writtenOn`,
`article`.`modified_on` AS `modifiedOn`,
`user`.`nickname` AS `userNickname`,
COUNT(`comment`.`index`) AS `commentCount`
FROM `study_bbs`.`articles` AS `article`
LEFT JOIN `study_member`.`users` AS `user` ON `article`.`user_email` = `user`.`email`
LEFT JOIN `study_bbs`.`comments` AS `comment` ON `article`.`index` = `comment`.`article_index`
WHERE `article`.`board_id` = #{boardId}
<if test="criterion != null and criterion.equals('title')">
AND REPLACE(`article`.`title`, ' ', '') LIKE CONCAT('%', REPLACE(#{keyword}, ' ', ''), '%')
</if>
<if test="criterion != null and criterion.equals('all')">
AND (REPLACE(`article`.`title`, ' ', '') LIKE CONCAT('%', REPLACE(#{keyword}, ' ', ''), '%')
OR REPLACE(`article`.`content`, ' ', '') LIKE CONCAT('%', REPLACE(#{keyword}, ' ', ''), '%'))
</if>
<if test="criterion != null and criterion.equals('nickname')">
AND BINARY `user`.`nickname` = #{keyword}
</if>
GROUP BY `article`.`index`
ORDER BY `article`.`index` DESC
LIMIT #{limit} OFFSET #{offset}
</select>
<!-- 페이징 -->
<select id="selectArticleCountByBoardId"
resultType="int">
SELECT COUNT(0)
FROM `study_bbs`.`articles`
<if test="criterion != null and criterion.equals('nickname')">
LEFT JOIN `study_member`.`users` AS `user` ON `articles`.`user_email` = `user`.`email`
</if>
WHERE `board_id` = #{boardId}
<if test="criterion != null and criterion.equals('title')">
AND REPLACE(`title`, ' ', '') LIKE CONCAT('%', REPLACE(#{keyword}, ' ', ''), '%')
</if>
<if test="criterion != null and criterion.equals('all')">
AND (REPLACE(`title`,' ', '') LIKE CONCAT('%', REPLACE(#{keyword}, ' ', ''), '%')
OR REPLACE(`content`, ' ', '') LIKE CONCAT('%', REPLACE(#{keyword}, ' ', ''), '%'))
</if>
<if test="criterion != null and criterion.equals('nickname')">
AND BINARY `user`.`nickname` = #{keyword}
</if>
</select>
IBbsMapper.java (인터페이스)
// 목록 만들기 + 페이징
ArticleReadVo[] selectArticlesByBoardId(@Param(value = "boardId") String boardId,
@Param(value = "criterion") String criterion,
@Param(value = "keyword") String keyword,
@Param(value = "limit") int limit,
@Param(value = "offset") int offset);
int selectArticleCountByBoardId(@Param(value = "boardId") String boardId,
@Param(value = "criterion") String criterion,
@Param(value = "keyword") String keyword);
BbsService.java
// 목록 만들기 + 페이징
public ArticleReadVo[] getArticles(BoardEntity board, PagingModel paging, String criterion, String keyword) {
return this.bbsMapper.selectArticlesByBoardId(board.getId(), criterion, keyword, paging.countPerPage,
(paging.requestPage -1) * paging.countPerPage);
}
BbsController.java
표시할 글이 없을 경우 페이지 기본값을 1로 지정해주기 위해
처음에는 아래와 같이 작성했으나
if (page <1) {
page = 1;
}
좀 더 있어보이고 보기 좋게
page = Math.max(1, page);
이렇게 수정하였다.
표시할 글이 없을 경우 1과 page 중 더 큰 값을 내놓게 되기 때문에
글이 없을 경우에도 1페이지가 기본 값이 된다.
System.out.println(page);
했을 때
원래는 null 이 찍히는데
위에
page = Math.max(1, page);
을 적어주기 때문에 1페이지가 찍힐 수 있게 된다.
//목록 만들기
@RequestMapping(value = "list",
method = RequestMethod.GET,
produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView getList(@RequestParam(value = "bid", required = false) String bid,
@RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
@RequestParam(value = "criterion", required = false) String criterion,
@RequestParam(value = "keyword", required = false) String keyword) {
// 페이징
page = Math.max(1, page);
ModelAndView modelAndView = new ModelAndView("bbs/list");
BoardEntity board = this.bbsService.getBoard(bid);
modelAndView.addObject("board", board);
if (board != null) {
// 검색을 했을 경우
int totalCount = this.bbsService.getArticleCount(board, criterion, keyword); // 페이지네이션 때문에 가지고 옴 (100개라면 10페이지 까지 표시해주기 위해서)
PagingModel paging = new PagingModel(totalCount, page);
modelAndView.addObject("paging", paging);
ArticleReadVo[] articles = this.bbsService.getArticles(board, paging, criterion, keyword); // 게시글
modelAndView.addObject("articles", articles);
}
return modelAndView;
}
실행결과
울고싶어라
'Spring Boot > 구현해보기' 카테고리의 다른 글
회원탈퇴 구현 (0) | 2022.12.28 |
---|---|
디렉토리 경로 인식 (0) | 2022.12.15 |
댓글 삭제하기 구현 (0) | 2022.11.27 |
댓글 수정하기 구현 (0) | 2022.11.27 |
게시글에 댓글 달기 구현 (5) | 2022.11.27 |