# 페이지 네이션
---
## Step 1: 첫 페이지 출력하기
![Imgur](https://i.imgur.com/Njz1pod.png)
### view
페이지네이션 UI를 추가. (ref. https://getbootstrap.com/docs/3.3/components/#pagination)
```
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center">
<li class="page-item">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
```
### Controller
리뷰 맵퍼 호출 변경
```
@RequestMapping(value = "/books/{id}", method = RequestMethod.GET)
public String show(@PathVariable int id, Model model, Principal principal, Paging paging) {
Book book = bookMapper.getBook(id);
model.addAttribute("book", book);
// 기존 리뷰들
List<Review> reviews = reviewMapper.getReviews(id, paging);
model.addAttribute("reviews", reviews);
model.addAttribute("paging", paging);
// 폼 태그에서 modelAttribute="review" 속성을 읽어올 수 있어야함.
if (!model.containsAttribute("review")) {
Review review = new Review();
review.setBookId(id);
String email = principal.getName();
int userId = userMapper.getUserIdByEmail(email);
review.setUserId(userId);
model.addAttribute("review", review);
}
// rating opts
Map<Integer, String> ratingOptions = new HashMap<Integer, String>();
ratingOptions.put(5, "★★★★★");
ratingOptions.put(4, "★★★★☆");
ratingOptions.put(3, "★★★☆☆");
ratingOptions.put(2, "★★☆☆☆");
ratingOptions.put(1, "★☆☆☆☆");
ratingOptions.put(0, "☆☆☆☆☆");
model.addAttribute("ratingOptions", ratingOptions);
return "books/show";
}
```
### Mapper
```
@Select("SELECT * FROM reviews WHERE book_id = #{bookId} ORDER BY id DESC LIMIT #{p.rows} OFFSET (#{p.index} - 1) * #{p.rows}")
@Results(value = {
@Result(property = "id", column = "id"),
@Result(property = "text", column = "text"),
@Result(property = "bookId", column = "book_id"),
@Result(property = "userId", column = "user_id"),
@Result(property = "user", column = "id", javaType = User.class, one = @One(select = "getUserById"))
})
List<Review> getReviews(@Param("bookId") int bookId, @Param("p") Paging paging);
```
### VO
```
public class Paging {
private int index; // 출력할 페이지 번호
private int rows; // 페이지 별 출랙 갯수
public Paging() {
index = 1;
rows = 50;
}
}
```
---
## Step 2: 페이징 번호 출력하기
![Imgur](https://i.imgur.com/rwkP51k.png)
### View
```
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center">
<!-- prev -->
<li class="page-item">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<!-- paging nums -->
<c:forEach var="i" begin="${paging.start}" end="${paging.end}" step="1">
<li class="page-item"><a class="page-link" href="#">${ i }</a></li>
</c:forEach>
<!-- next -->
<li class="page-item">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
```
### Controller
```
@RequestMapping(value = "/books/{id}", method = RequestMethod.GET)
public String show(@PathVariable int id, Model model, Principal principal, Paging paging) {
Book book = bookMapper.getBook(id);
model.addAttribute("book", book);
// 기존 리뷰들
List<Review> reviews = reviewMapper.getReviews(id, paging);
model.addAttribute("reviews", reviews);
// 페이징
paging.setTotal(reviewMapper.getReviewsCnt(id));
model.addAttribute("paging", paging);
// 폼 태그에서 modelAttribute="review" 속성을 읽어올 수 있어야함.
if (!model.containsAttribute("review")) {
Review review = new Review();
review.setBookId(id);
String email = principal.getName();
int userId = userMapper.getUserIdByEmail(email);
review.setUserId(userId);
model.addAttribute("review", review);
}
// rating opts
Map<Integer, String> ratingOptions = new HashMap<Integer, String>();
ratingOptions.put(5, "★★★★★");
ratingOptions.put(4, "★★★★☆");
ratingOptions.put(3, "★★★☆☆");
ratingOptions.put(2, "★★☆☆☆");
ratingOptions.put(1, "★☆☆☆☆");
ratingOptions.put(0, "☆☆☆☆☆");
model.addAttribute("ratingOptions", ratingOptions);
return "books/show";
}
```
### Mapper
```
@Select("SELECT COUNT(*) FROM reviews WHERE book_id = #{bookId}")
int getReviewsCnt(int id);
```
### VO
```
public class Paging {
private int index; // 출력할 페이지 번호
private int rows; // 페이지 별 출력 갯수
private int total; // 검색 결과 총 갯수
public Paging() {
index = 1;
rows = 50;
}
public void setTotal(int total) {
this.total = total;
}
public int getStart() {
return 1;
}
public int getEnd() {
int totalPagesCnt = (int) Math.ceil(1.0 * total / rows);
return totalPagesCnt;
}
}
```
---
## Step 3: 페이지 개수 제한하기
아래와 같이 너무많은 페이지가 보여지지 않게 하려면?
![Imgur](https://i.imgur.com/0Vfflv2.png)
### VO
```
public class Paging {
private int index; // 출력할 페이지 번호
private int rows; // 페이지 별 출력 갯수
private int total; // 검색 결과 총 갯수
private int pageLimit; // 출력할 페이지 번호 갯수
public Paging() {
index = 1;
rows = 1;
pageLimit = 5;
}
public void setTotal(int total) {
this.total = total;
}
public int getStart() {
return 1;
}
public int getEnd() {
int totalPagesCnt = (int) Math.ceil(1.0 * total / rows);
int start = getStart();
int end = start + pageLimit - 1;
return Math.min(end, totalPagesCnt);
}
}
```
### 결과화면
![Imgur](https://i.imgur.com/Fv5xeVb.png)
---
## Step 4: 해당 페이지 내용 링크 걸기
![Imgur](https://i.imgur.com/PihGxby.png)
### View
```
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center">
<!-- prev -->
<li class="page-item">
<a class="page-link" href="<c:url value="/books/${ book.id }?pageNum=1#reviews" />" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<!-- paging nums -->
<c:forEach var="i" begin="${paging.start}" end="${paging.end}" step="1">
<li class="page-item" id="review-page-index-${i}"><a class="page-link" href="<c:url value="/books/${ book.id }?pageNum=${i}#reviews" />">${ i }</a></li>
</c:forEach>
<!-- next -->
<li class="page-item">
<a class="page-link" href="<c:url value="/books/${ book.id }?pageNum=${paging.lastPageNum}#reviews" />" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
<Script>
$.urlParam = function(name){
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (results == null){
return null;
}
else{
return results[1] || 0;
}
}
var pageNum = $.urlParam('pageNum')
if (pageNum == null) {
pageNum = 1;
}
$("#review-page-index-" + pageNum).addClass("active");
</Script>
```
### VO
```
public class Paging {
private int index; // 출력할 페이지 번호
private int rows; // 페이지 별 출랙 갯수
private int total; // 검색 결과 총 갯수
private int pageLimit; // 출력할 페이지 번호 갯수
public Paging() {
index = 1;
rows = 3;
pageLimit = 5;
}
public void setTotal(int total) {
this.total = total;
}
public int getStart() {
return Math.max(1, index - (pageLimit / 2));
}
public int getEnd() {
int totalPagesCnt = (int) Math.ceil(1.0 * total / rows);
int start = getStart();
int end = start + pageLimit - 1;
return Math.min(end, totalPagesCnt);
}
public int getLastPageNum() {
return (int) Math.ceil(1.0 * total / rows);
}
public void setIndex(int pageNum) {
index = pageNum;
}
@Override
public String toString() {
return "Paging [index=" + index + ", rows=" + rows + ", total=" + total + ", pageLimit=" + pageLimit + "]";
}
}
```
### Controller
```
// https://stackoverflow.com/questions/41620395/spring-set-default-pathvariable
// https://acuriouscoder.net/optional-path-variables-in-spring-mvc/
// https://stackoverflow.com/questions/17934972/get-query-string-values-in-spring-mvc-controller
@RequestMapping(value = "/books/{id}", method = RequestMethod.GET)
public String show(@PathVariable int id, Model model, Principal principal, Paging paging, @RequestParam(value="pageNum", required=false) Optional<Integer> pageNum) {
Book book = bookMapper.getBook(id);
model.addAttribute("book", book);
// 기존 리뷰들
if (pageNum.isPresent()) {
paging.setIndex(pageNum.get());
}
List<Review> reviews = reviewMapper.getReviews(id, paging);
model.addAttribute("reviews", reviews);
// 페이징
paging.setTotal(reviewMapper.getReviewsCnt(id));
model.addAttribute("paging", paging);
// 폼 태그에서 modelAttribute="review" 속성을 읽어올 수 있어야함.
if (!model.containsAttribute("review")) {
Review review = new Review();
review.setBookId(id);
String email = principal.getName();
int userId = userMapper.getUserIdByEmail(email);
review.setUserId(userId);
model.addAttribute("review", review);
}
// rating opts
Map<Integer, String> ratingOptions = new HashMap<Integer, String>();
ratingOptions.put(5, "★★★★★");
ratingOptions.put(4, "★★★★☆");
ratingOptions.put(3, "★★★☆☆");
ratingOptions.put(2, "★★☆☆☆");
ratingOptions.put(1, "★☆☆☆☆");
ratingOptions.put(0, "☆☆☆☆☆");
model.addAttribute("ratingOptions", ratingOptions);
return "books/show";
}
```
---
## Step 5: 리뷰가 바로 보여 질 수 있게..
### View
```
<table class="table table-stripped" id="reviews">
...
<nav aria-label="Page navigation">
<ul class="pagination">
<!-- first -->
<li><a href="<c:url value="/books/${ book.id }?pageNum=1#reviews" />" aria-label="Previous"><spanaria-hidden="true">«</span></a></li>
<c:forEach var="i" begin="${paging.start}" end="${paging.end}" step="1">
<li><a href="<c:url value="/books/${ book.id }?pageNum=${i}#reviews" />">${i}</a></li>
</c:forEach>
<!-- last -->
<li><a href="<c:url value="/books/${ book.id }?pageNum=${paging.lastPageNum}#reviews" />" aria-label="Next"> <span aria-hidden="true">»</span></a></li>
</ul>
</nav>
```
---
## Ref.
+ https://okky.kr/article/283315
+ http://handcoding.tistory.com/15
+ https://handam.tistory.com/41