이번 글은 게시글 리스트 페이지에 페이징 기능을 적용한 과정을 담았다.
페이징을 적용하기 전에는 불러온 게시글 목록이 한 번에 모두 표시되어 사용자가 많은 양의 정보를 한꺼번에 보게 되었다. 그러나 페이징 기능을 적용한 후, 한 페이지당 10개의 게시글만 보여주도록 설정함으로써, 페이지가 더 깔끔하고 가독성이 좋아졌다.
Controll Class
//내가 쓴 글 보기
@GetMapping("/myPost")
public String myPost(HttpSession session, Model model, @RequestParam(defaultValue = "1") int page) {
imMyPostListService.myPostList(session, model, page);
return "post/myPost";
}
처음에 생각해야 될 것이 게시글을 불러오는 페이지로 이동 시 게시글이 바로 나타나야 함과 동시에 페이지는 1번이 체크되어 있어야한다. 즉, 다른페이지에서 게시글 리스트 페이지로 이동시에는 게시물 목록 페이지인 1번을 클릭하는 과정이 없으므로
@RequestParam(defaultValue = "1") int page
page값을 전달할 떄 값이 없다면 (defaultValue = "1") 로 1을 기본 값으로 설정을 해준다.
이후 다음 게시물 리스트로 넘어가기 위해 현재 페이지 외에 다른 페이지를 클릭 시 int page에는 클릭한 데이터의 값이 들어간다.
Service Class
@Service
@RequiredArgsConstructor
public class ImMyPostListService {
private final ImPostMapper imPostMapper;
public void myPostList(HttpSession session, Model model, int page) {
//로그인 된 유저의 유저코드
String userCode = (String) session.getAttribute("userCode");
//페이지당 게시글 수 설정
int postPerPage = 10;
//전체 게시글 수 가져오기
int totalPost= imPostMapper.countMyPostList(userCode);
//페이징 데이터 가져오기
ImPostPageVO pagination = new ImPostPageVO(page, totalPost, postPerPage);
//현재 페이지에 해당하는 게시글 가져오기
int offset = (page - 1) * postPerPage;
List<ImPostListDto> myPostList = imPostMapper.myPostList(userCode, offset, postPerPage);
//모델에 담아 뷰로 전달
model.addAttribute("myPostList", myPostList);
model.addAttribute("pagination", pagination);
}
}
int postPerPage = 10;
페이지별 나타낼 게시물의 갯수를 정해준다.
int totalPost= imPostMapper.countMyPostList(userCode);
mybatis mapper를 이용해 게시물의 총 갯수를 불러온다.
ImPostPageVO pagination = new ImPostPageVO(page, totalPost, postPerPage);
이전 1 2 3 4 5 6 7 8 9 10 다음
위의 예시와 같은 게시물 페이징 처리를 위한 클래스로서
page = 현재 페이지
totalPost = 총 게시글의 갯수
postPerPage = 한 페이지당 게시글 갯수
의 데이터를 넘겨
총 게시물 및 페이지당 출력되는 게시글의 갯수를 계산하여 총 페이지 및 몇개의 페이지를 나타낼지 계산해주는 클래스이다. (ex 총 게시물의 갯수는 15개이고 한 페이지당 10개를 나타낼거라면 1 2 의 페이지만 나와야한다.)
int offset = (page - 1) * postPerPage;
List<ImPostListDto> myPostList = imPostMapper.myPostList(userCode, offset, postPerPage);
offset을 구한 이유는 1페이지에선 게시글이 1~10이 나와야하고, 2페이지에선 게시글이 11~20이 나와야하므로
쿼리의 limit을 사용하기 위함이다.
int offset = (page - 1) * postPerPage;
ex) 현재 페이지가 1번이라면 (1-1)*10의 식으로 인해 결과값이 0이된다.
List<ImPostListDto> myPostList = imPostMapper.myPostList(userCode, offset, postPerPage);
그러면 mapper로 전달하여 쿼리문 마지막에 limit 0(offset),10(postPerPage / 여기는 위에서 10개로 지정해줬다.)을 입력되게 하여 데이터를 불러온다면
index값 0부터 10개의 데이터를 받는다
만약 다음 게시글을 보기위해 3 페이지를 눌렀다고 가정하면,
(3-1) * 10 의 식으로 인해 20이 된다.
그러면 limit 20, 10 으로 인해 index 20번부터 10개의 데이터가 출력되는 것이다.
그럼 여기까지는 게시글 리스트 페이지에서 다음 게시글을보기 위해 페이지를 눌렀을 떄 그 페이지 맞게 10개의 게시글이 리스트업 되는거까지는 확인이 됏다. 그러면 이제 다음 게시글을 보기 위한 페이징 설정이 어떻게 되는지 알아보겠다.
Page VO Class
@Getter
public class ImPostPageVO {
private int currentPage; // 현재 페이지
private int totalPages; // 전체 페이지 수
private int startPage; // 페이지 네비게이션의 시작 페이지
private int endPage; // 페이지 네비게이션의 끝 페이지
public ImPostPageVO(int currentPage, int totalPost, int postPerPage) {
this.currentPage = currentPage;
// 전체 페이지 수 계산
this.totalPages = (int) Math.ceil((double) totalPost / postPerPage);
// 페이지 네비게이션 계산 (한 블록에 10페이지 표시)
int pageBlock = 10;
this.startPage = ((currentPage - 1) / pageBlock) * pageBlock + 1;
this.endPage = Math.min(this.startPage + pageBlock - 1, this.totalPages);
}
}
// 전체 페이지 수 계산
this.totalPages = (int) Math.ceil((double) totalPost / postPerPage);
총 게시글 갯수에 따른 페이지 갯수를 구해준다
ex) 총 게시글 갯수가 25개면서 한 페이지당 10개씩 나타낸다는 조건이라면, 3이 나와야한다.
이 때, int로 정의를 해버리면 결과값이 2가 나와 최대 페이지는 2로 설정된다. 그러면 페이지당 10개씩 나타내기로 한다면 마지막 5개의 게시글은 볼 수가 없다. 그래서 double로 정의한 후 Math.ceil의 올림을 이용해 소수점이 있을 경우엔 올림을하여 나머지 게시글도 보일 수 있는 마지막 페이지를 만들어 안보이는 게시글이 없도록 만들어줘야 한다.
// 페이지 네비게이션 계산 (한 블록에 10페이지 표시)
int pageBlock = 10;
this.startPage = ((currentPage - 1) / pageBlock) * pageBlock + 1;
this.endPage = Math.min(this.startPage + pageBlock - 1, this.totalPages);
int pageBlock = 10;
한 블록에 보여줄 페이지의 갯수
ex) 1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
this.startPage = ((currentPage - 1) / pageBlock) * pageBlock + 1;
이건 현재 머무르고 있는 페이지의 제일 왼쪽에 위치한 첫번째 페이지를 나타내는 것인데(ex 1 2 3 4 5 6 7 8 9 10 중 1을 구하기 위함),
페이지 1에 위치하고 있다는 가정을 하면
((1-1)/10)*10+1 의 결과값인 1이 첫번째 페이지인 것이고 (ex 1 2 3 4 5 6 7 8 9 10),
페이지 6에 위치하면 위와 같이
((6-1)/10)*10+1 의 결과값인 1이 첫번째 페이지인 것이고 (ex 1 2 3 4 5 6 7 8 9 10),
만약 페이지가 10 이상인 15에 위치하고있다고 가정을 하면
((15-1)/10)*10+1 의 결과값인 11이 첫번째 페이지가 되는 것이다(ex 11 12 13 14 15 16 17 18 19 20).
위 과정을 거쳐 첫번째 페이지를 구하고,
this.endPage = Math.min(this.startPage + pageBlock - 1, this.totalPages);
첫번째 페이지를 구하는 것처럼 마지막 페이지도 구해야되는데, 위에 한 블록당 10개의 페이지가 나타나도록 했으니
현재 페이지의 위치가 1~10이라면 시작페이지값이 구해지니,
시작페이지 + 한 블록당 페이지갯수 -1 로 마지막 페이지를 구한다.
(만약 현재 위치하는 페이지가 1~10일 경우, 시작페이지는 1이되니, 1+10-1의 결과값인 10이 마지막 페이지가 되는것이다.)
근데 여기서 중요한점이
만약 게시글 불러온 갯수의 값이 100 단위로 딱 맞아떨어지지 않을 경우 페이지 마지막 블록의 마지막 페이지 값은
시작페이지 + 한 블록당 페이지 갯수 -1 의 값이 아닐 것이다.
ex) 게시글이 총 120개가 있다고 가정한다면
첫번째 페이질 블록 : 1 2 3 4 5 6 7 8 9 10
두번째 페이질 블록 : 11 12
그러면 마지막 페이지를 구하는 것은 this.startPage + pageBlock - 1 의 결과값과 앞서 구한 totalPages값 중 Math.min을 이용하여 작은 값을 마지막 endPage로 설정해야한다.
JSP
<!-- 페이지 번호 -->
<c:choose>
<c:when test="${not empty myPostList}">
<div class="pagination">
<c:if test="${pagination.currentPage > 1}">
<a href="?page=${pagination.currentPage - 1}">이전</a>
</c:if>
<c:forEach begin="${pagination.startPage}" end="${pagination.endPage}" var="pageNum">
<c:choose>
<c:when test="${pageNum == pagination.currentPage}">
<span class="current">${pageNum}</span>
</c:when>
<c:otherwise>
<a href="?page=${pageNum}">${pageNum}</a>
</c:otherwise>
</c:choose>
</c:forEach>
<c:if test="${pagination.currentPage < pagination.totalPages}">
<a href="?page=${pagination.currentPage + 1}">다음</a>
</c:if>
</div>
</c:when>
</c:choose>
<c:when test="${not empty myPostList}">
만약 게시물 데이터가 존재한다면,
<c:if test="${pagination.currentPage > 1}">
<a href="?page=${pagination.currentPage - 1}">이전</a>
</c:if>
현재 페이지가 1 초과의 값이라면 이전버튼이 생기고, 이전버튼을 누르면 현재페이지-1 의 값으로 이동
<c:forEach begin="${pagination.startPage}" end="${pagination.endPage}" var="pageNum">
<c:choose>
<c:when test="${pageNum == pagination.currentPage}">
<span class="current">${pageNum}</span>
</c:when>
<c:otherwise>
<a href="?page=${pageNum}">${pageNum}</a>
</c:otherwise>
</c:choose>
</c:forEach>
<c:forEach begin="${pagination.startPage}" end="${pagination.endPage}" var="pageNum">
시작페이지와 끝페이지를 포함한 사이에 존재하는 모든 값을 나타내준다.
<c:when test="${pageNum == pagination.currentPage}">
<span class="current">${pageNum}</span>
이 중 현재 페이지와 같은 숫자에는 class="current" 를 적용하여 css로 현재 페이지임을 표시하는 방법
<c:otherwise>
<a href="?page=${pageNum}">${pageNum}</a>
</c:otherwise>
만약 다른 페이지를 클릭 시 그 페이지로 이동
<c:if test="${pagination.currentPage < pagination.totalPages}">
<a href="?page=${pagination.currentPage + 1}">다음</a>
</c:if>
그리고 현재 페이지가 제일 마지막 페이지보다 작다면 다음 버튼이 생기도록한다.
다음 버튼 클릭시 현재 페이지+1 로 이동
결과
사용자가 페이지를 누를때마다 이 글에서 정리된 과정이 계속해서 이루어져 원하는 페이지 및 그 페이지에 맞는 게시글이 보여진다.
'개인프로젝트 > I'm' 카테고리의 다른 글
Spring HandlerInterceptor 비로그인 시 페이지 진입이 불가한 페이지 설정 (0) | 2024.12.29 |
---|---|
Spring 게시글 작성하기 > DB 게시글 저장 (+MyBatis, CKeditor) (0) | 2024.12.29 |
Spring 회원가입 후 로그인/로그아웃 HttpSession 적용 (0) | 2024.12.27 |
Spring JPA 회원가입 + 비밀번호 암호화 Spring security(+회원 UUID 고유코드 생성)(241229 서버 2차 조건 검증 기능 추가) (0) | 2024.12.24 |
Spring 회원가입 시 인증번호 일치 여부 확인 / redis 사용 (2) | 2024.12.21 |