저번시간에는 매장목록화면에 무한스크롤을 이용한 페이징 기능을 추가했었습니다
이번시간에는 주문목록화면에 페이징 기능을 추가해보겠습니다. 이번에는 무한스크롤을
이용한 비동기페이징 방식이 아니라 페이지 번호를 통한 동기페이징 방식으로
진행하도록 하겠습니다
일단 구현방향에 간단히 설명하자면 우리는 현재 주문목록페이지인
localhost:8080/orderList로 접근시 DB로부터 모든 주문정보를 가져와
JSTL의 foreach문을 통해 화면에 모두 뿌려주고 있습니다.
저번시간에도 말했듯이 주문건수가 많으면 로딩에 많은시간이 걸리기 때문에
페이징처리를 해줘야하는데 주문목록에서 무한스크롤 비동기 페이징을 사용할 경우
만약 수천건의 주문정보가 있으면 맨처음 주문이 언제인지 확인하기 위해서는
계속해서 스크롤을 내려 데이터를 불러와야 합니다
따라서 버튼형 페이징을 사용할것이며 이때 비동기식이든 동기식이든 서버측의 로직은
똑같습니다. 일단 OrderController를 수정해주도록 하겠습니다
//주문목록
@GetMapping({"/orderList","/orderList/{page}"})
public String orderList(@AuthenticationPrincipal CustomUserDetails principal, Model model, @PathVariable(required = false) Integer page) {
if (principal == null) {
System.out.println("비로그인");
} else {
System.out.println("로그인");
long userId = principal.getId();
Page p = new Page(page);
List<OrderListDto> orderList = orderService.orderList(userId, p);
if (orderList.size() == 0) {
return "order/orderList";
}
List<List<CartDto>> cartDtoList = new ArrayList<>();
for (int i=0;i<orderList.size();i++) {
cartDtoList.add(FoodInfoFromJson.foodInfoFromJson(orderList.get(i).getFoodInfo()));
}
p.totalPage(orderList.get(0).getListCount());
model.addAttribute("cartList", cartDtoList);
model.addAttribute("orderList", orderList);
//추가
model.addAttribute("page", p);
}
return "order/orderList";
}
Mapping주소에 /orderList/{page}
를 추가해줬는데 주문목록의 기본 주소는
/orderList
입니다. 기본주소의 경우 1페이지를 나타내어야 하며 2페이지 버튼을
클릭할경우 /orderList/2
로 이동이 됩니다. 이때 @PathVariable
을 이용하여
page에 2라는 값이 할당되는데 반드시 타입을 int가 아닌 Integer로 선언해야합니다
그 이유는 기본주소인 /orderList
로 접근할경우 주소에 page에 들어갈 값이 존재하지
않기 때문에 null값을 가지게 되는데 int형의 경우 null값을 가질수 없습니다
Integer는 null값을 가질수 있는 정수형이기 때문에 Integer로 선언하여 이 값이 null일
경우 1을 할당해줄겁니다. 그외의 코드는 밑에서 설명하도록 하겠습니다
지난시간에 추가한 Page클래스에 버튼식 페이징을 위한 코드를 추가해줍시다
@Data
public class Page {
//비동기 무한스크롤
private int view = 10; // 화면에 출력할 목록 수
private int firstList; // 페이지 첫번째 목록
private int lastList; // 페이지 마지막 목록
//동기 페이지 버튼식
private int pageCount = 5; // 페이지 이동 버튼 갯수
private int firstPage; // 화면의 첫번째 페이지 1~5 => 1, 6~10 => 6
private int lastPage; // 화면의 마지막 페이지 1~5 => 5, 6~10 => 10
private int prevPage; // 이전페이지 버튼클릭
private int nextPage; // 다음페이지 버튼클릭
private int nowPage; // 현재페이지
private int totalPage; // 총페이지 수
public Page() {
this(1);
}
public Page(int movePage) {
page(movePage, view);
}
public Page(int movePage, int view) {
page(movePage, view);
}
//버튼식 동기 페이징 추가 메서드
public Page(Integer page) {
int movePage = 1;
if(page != null) {
movePage = page;
}
page(movePage, view);
}
public void page(int movePage, int view) {
this.firstList = (view * movePage) - view + 1;
this.lastList = movePage * view;
//버튼식 동기 페이징 추가
nowPage = movePage;
firstPage = movePage - (movePage - 1) % pageCount;
lastPage = firstPage + pageCount - 1;
prevPage = firstPage - 1;
nextPage = firstPage + pageCount;
}
//버튼식 동기 페이징 추가 메서드
public void totalPage(int listCount) {
if(listCount % view == 0) {
totalPage = listCount / view;
} else {
totalPage = listCount / view + 1;
}
}
}
Page(Integer page)
생성자의 경우 변수값이 null일 경우 movePage에 1을
그 외의 경우 변수를 할당하여 page(int movePage, int view)
메서드를 호출합니다
page(int movePage, int view)
메서드를 살펴보면 만약 사용자가 3번 페이지를 클릭하면
Integer page
값은 3이므로 movePage=3, nowPage=3이 됩니다
nowPage는 현재 요청한 페이지 번호를 나타내고 페이지 이동 버튼 갯수를 나타내는
pageCount를 5로 설정해뒀기 때문에 1~5까지의 버튼이 나타나야 하고
nowPage가 7이면 6~10 , nowPage가 13이면 11~15까지의 버튼이 보여야합니다
따라서 현재 페이지를 기준으로
firstPage는 현재페이지 - (현재페이지 - 1) % 출력 할 페이지 갯수
lastPage는 firstPage + 출력할페이지갯수 -1이 됩니다
이전버튼을 나타내는 prevPage는 firstPage의 바로 이전이므로 -1
다음버튼을 나타내는 nextPage는 firstPage+pageCount 또는 lastPage+1
로 나타낼수 있습니다.
이제 만약 사용자가 7페이지를 요청할 경우
firstPage = 6, lastPage=10 , prevPage= 5, nextPage=11의 값을 가지게 됩니다
이 상태에서 사용자가 이전버튼을 누를경우 /orderList/5
페이지로 이동되어
firstPage = 1 , lastPage=5 , prevPage=0 , nextPage = 6의 값을 가지게 되는데
prevPage가 0일경우에는 이전페이지 버튼이 보이지 않게 해줄겁니다
이렇게만 구현할 경우 만약 내가 총 주문횟수가 80번일경우 한 페이지당 10개의 목록이
보이므로 1,2,3,4,5|다음 -> 다음버튼클릭 -> 이전|6,7,8이 아닌
이전|6,7,8,9,10|다음 이렇게 나오게 됩니다. 따라서 우리는 총 페이지갯수를 알아야하며
totalPage(int listCount)
메서드에서는 현재 로그인된 사용자의 총 주문횟수인
listCount를 통하여 총 페이지 갯수인 totalPage를 구합니다
이제 view에서 totalPage<=lastPage
일경우 다음버튼을 없애고 페이지 번호 추가를
막으면 정상적으로 페이지 버튼이 나타나는걸 볼 수 있습니다.
totalPage를 구하기 위해 기존 Dto에 listCount 변수를 추가해주도록 합시다
private int listCount; // 목록 총 갯수
// 전체 주문 목록
public List<OrderListDto> orderList(long userId, Page p) {
Map<String, Object> map = new HashMap<>();
map.put("userId", userId);
map.put("firstList", p.getFirstList());
map.put("lastList", p.getLastList());
System.out.println("첫번째 목록 = " + p.getFirstList() + " 마지막 목록 = " + p.getLastList());
System.out.println("첫번째 = " + p.getFirstPage() + " 마지막 = " + p.getLastPage() );
System.out.println("이전페이지 = " + p.getPrevPage());
System.out.println("다음페이지 = " + p.getNextPage());
return orderMapper.orderList(map);
}
// 주문 전체 목록 가져오기
public List<OrderListDto> orderList(Map<String, Object> map);
<select id="orderList" resultType="com.han.delivery.dto.OrderListDto">
SELECT * FROM (
SELECT ROWNUM R
,O.*
FROM (SELECT count(*) over() list_count,
o.order_num,
o.user_id,
o.order_date,
o.pay_method,
o.delivery_status,
o.delivery_address1,
o.delivery_address2,
o.delivery_address3,
o.store_id,
o.total_price,
o.used_point,
o.request,
d.food_info,
s.store_name,
s.store_img,
s.store_thumb,
s.delivery_tip,
r.review_content,
r.score,
r.review_img
FROM dl_order_user o
LEFT JOIN (SELECT ORDER_NUM,
LISTAGG(FOOD_INFO, '/') food_info
FROM dl_ORDER_DETAIL_USER
GROUP BY ORDER_NUM) d
ON o.order_num = d.order_num
LEFT JOIN dl_store s
ON o.store_id = s.id
LEFT JOIN dl_review r
ON o.order_num = r.order_num
WHERE o.user_id = #{userId}
ORDER BY o.order_date desc
) O
)
WHERE R BETWEEN #{firstList } AND #{lastList }
</select>
쿼리에는 변경사항은 없고 where절만 붙여줬습니다
이부분은 이전 포스팅과 동일하니 넘어가도록 하겠습니다
이제 주문목록화면에 페이지이동버튼을 추가해줘야 합니다
기존 orderList.jsp에 추가해도 상관없지만 주문목록 페이지뿐만 아니라
다른곳에서도 재사용하기 위해 views/layout폴더에 pageBox라는 이름으로
생성한후 orderList.jsp의 하단부분에 include해주도록 하겠습니다
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<style>
.page_box {display: flex; justify-content: center; margin: 20px 0; }
.page_box li { border: 1px solid #999; border-right: none; width: 35px; height: 35px; text-align: center; line-height: 35px; }
.page_box li:last-child { border-right: 1px solid #999; }
.page_box li a { display: block; width: 100%; height: 100%; }
.now_page { background: #30DAD9; color: #fff; cursor: default; }
.now_page:hover { color: #fff; }
@media(max-width :767px) {
.page_box { margin-top: 20px; }
.page_box li { width: 25px; height: 25px; line-height: 25px; font-size: 12px; }
}
</style>
<ul class="page_box">
<c:if test="${page.pageCount < page.firstPage }">
<li><a href="/orderList/${page.prevPage }">이전</a></li>
</c:if>
<c:forEach begin="${page.firstPage }" end="${page.firstPage + page.pageCount - 1 }" var="i">
<c:if test="${i <= page.totalPage}">
<c:if test="${i != page.nowPage }">
<li><a href="/orderList/${i} ">${i }</a></li>
</c:if>
<c:if test="${i == page.nowPage }">
<li><a class="now_page" onclick="return false;" href="/orderList/${i }">${i }</a></li>
</c:if>
</c:if>
</c:forEach>
<c:if test="${page.firstPage + page.pageCount <= page.totalPage }">
<li><a href="/orderList/${page.nextPage }">다음</a></li>
</c:if>
</ul>
코드가 실행되는 로직은 위에서 다 설명했으므로 생략하겠습니다
이제 orderList.jsp의 </main>
바로 위에
<%@ include file="/WEB-INF/views/layout/pageBox.jsp" %>
를
추가해주고 주문목록 화면에 들어가서 잘 동작하는지 확인해봅시다