9/22/2019

스프링 게시판 만들기 #11. 페이징 구현 3

지금 이대로 사용해도 기능적으로는 문제가 없지만, 좀 난잡한감이 없지않아있습니다.

페이징을 만드는 저 코드를 하나의 클래스로 떼어내도록 하겠습니다.

com.board.domain 을 우클릭한 후 New → Class를 선택합니다.

페이징을 관리하는 클래스니까, 이름은 Page 로 하겠습니다.

이렇게하면 지나가는 사람 붙잡고 물어봐도, 페이징과 관련된 클래스라는것을 알것입니다.

BoardController에서 작업한 페이징 기능을 Page로 옮기는것이기 때문에 이렇게 두개를 켜놓고 작업했습니다.

먼저 컨트롤러에서 사용한 각 변수를 선언해줍니다.

// 현재 페이지 번호
private int num;

// 게시물 총 갯수
private int count;

// 한 페이지에 출력할 게시물 갯수
private int postNum = 10;

// 하단 페이징 번호 ([ 게시물 총 갯수 ÷ 한 페이지에 출력할 갯수 ]의 올림)
private int pageNum;

// 출력할 게시물
private int displayPost;

// 한번에 표시할 페이징 번호의 갯수
private int pageNumCnt = 10;

// 표시되는 페이지 번호 중 마지막 번호
private int endPageNum;

// 표시되는 페이지 번호 중 첫번째 번호
private int startPageNum;

// 다음/이전 표시 여부
private boolean prev;
private boolean next;

다음은 getter와 setter를 설정합니다.

이때, setter는 2개만 필요로 합니다. 데이터 입력이 필요한건 현재 페이지 num, 게시물 총 갯수 count 이렇게 2개라서 그렇습니다.

마찬가지로 getter는 7개만 필요로하는데, 제가 실수로 2개를 더 넣었네요. Page 클래스에서 getter의 역할은 데이터를 출력하는, 그러니까 현재 컨트롤러에서 model에 넣어지는 데이터들입니다.

public void setNum(int num) {
 this.num = num;
}

public void setCount(int count) {
 this.count = count;
}

public int getCount() {
 return count;
}

public int getPostNum() {
 return postNum;
}

public int getPageNum() {
 return pageNum;
}

public int getDisplayPost() {
 return displayPost;
}

public int getPageNumCnt() {
 return pageNumCnt;
}

public int getEndPageNum() {
 return endPageNum;
}

public int getStartPageNum() {
 return startPageNum;
}

public boolean getPrev() {
 return prev;
} 

public boolean getNext() {
 return next;
}

다음은 데이터들을 계산하는 메서드를 만듭니다.

게시물 총 갯수를 알고난 시점부터 계산을 할 수 있으므로, setCount에서 메서드를 호출하면 됩니다.

dataCalc 메서드는 이번에 새로 만드는게 아니라, 기존 컨트롤러에 있는 코드를 그대로 가져와서 살짝 수정만해주면 됩니다.

private void dataCalc() {
 
 // 마지막 번호
 endPageNum = (int)(Math.ceil((double)num / (double)pageNumCnt) * pageNumCnt);
 
 // 시작 번호
 startPageNum = endPageNum - (pageNumCnt - 1);
 
 // 마지막 번호 재계산
 int endPageNum_tmp = (int)(Math.ceil((double)count / (double)pageNumCnt));
 
 if(endPageNum > endPageNum_tmp) {
  endPageNum = endPageNum_tmp;
 }
 
 prev = startPageNum == 1 ? false : true;
 next = endPageNum * pageNumCnt >= count ? false : true;
 
 displayPost = (num - 1) * postNum;
 
}

이제 컨트롤러에서 페이징이 있는 게시물 목록(getListPage)의 내부 코드를 모두 주석처리합니다.

Page형의 page변수를 생성합니다.

처음 생성하면 인식을 못하므로, Ctrl (컨트롤) + Shift (쉬프트) + O 를 눌러서 임포트 시켜줍니다. 혹시나해서 언급하지만 숫자 0이 아닌 영어 O입니다.

그럼 코드의 최상단부에 임포트되면서 에러가 없어집니다.

Page에 현재 페이지인 num, 게시물 총 갯수인 service.count()를 넣어주면 클래스 내부에서 계산을 합니다.

이렇게 계산된 데이터는 page.getDisplaytPost() 처럼 호출하여 사용할 수 있습니다.

page.setNum(num);
page.setCount(service.count());  

List list = null; 
list = service.listPage(page.getDisplayPost(), page.getPostNum());

model.addAttribute("list", list);   
model.addAttribute("pageNum", page.getPageNum());

model.addAttribute("startPageNum", page.getStartPageNum());
model.addAttribute("endPageNum", page.getEndPageNum());
 
  model.addAttribute("prev", page.getPrev());
model.addAttribute("next", page.getNext());  

model.addAttribute("select", num);

페이징 기능을 클래스로 분리하였기 때문에, 컨트롤러의 코드가 짧아졌습니다.

빨간박스로 표시한 부분은 모두 page 의 데이터입니다. 그렇다면 이것도 줄일 수 있겠군요.

기존 코드는 주석처리하고, 새로운 코드 한줄을 추가합니다.

model.addAttribute("page", page);

이렇게하면 page의 데이터 전부를 뷰(view)로 전달할 수 있습니다.

listPage.jsp를 열어 페이징 관련 코드쪽을 수정하겠습니다.

기존에 사용했던 모델명 앞에 page. 을 붙여줍니다.

<c:if test="${page.prev}">
 <span>[ <a href="/board/listPage?num=${page.startPageNum - 1}">이전</a> ]</span>
</c:if>

<c:forEach begin="${page.startPageNum}" end="${page.endPageNum}" var="num">
 <span>
 
  <c:if test="${select != num}">
   <a href="/board/listPage?num=${num}">${num}</a>
  </c:if>    
  
  <c:if test="${select == num}">
   <b>${num}</b>
  </c:if>
    
 </span>
</c:forEach>

<c:if test="${page.next}">
 <span>[ <a href="/board/listPage?num=${page.endPageNum + 1}">다음</a> ]</span>
</c:if>

page 의 데이터가 그대로 넘어왔기 때문에 page 내부의 데이터를 그대로 호출하여 사용할 수 있습니다.

이제 프로젝트를 실행해보면, 기존과 동일하게 동작하는것을 확인할 수 있습니다.

다른 페이지로 이동하여도 정상적으로 동작합니다.

게시물 수정
  1. 정말 좋은 강의 글 감사합니다! 덕분에 스프링 입문이 훨씬 편해졌습니다! 굉장히 잘 배우고 재밌어하고 갑니다!

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      다른 글에도 댓글 감사합니다(__)


      게시물 날짜는 예전이지만 사실 며칠전에 쓴건데(...)
      업로드 속도를 조금씩 올리겠습니다ㅠㅠ

      삭제
  2. "이렇게하먄 지나가는 사람 붙잡고 물어봐도, 페이징과 관련된 클래스라는것을 알것입니다."

    ㅋㅋㅋㅋ이거 저만 웃긴가요 ㅋㅋㅋㅋㅋㅋ

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      가상 이상적인 명칭은, 지나가는 사람(개발자)보고 이게 뭐겠냐고 물어봤을 때... 의도한거랑 같거나 비슷한 대답이 나오는거라 생각합니다+_+

      삭제
  3. dataCalc() 메소드는 따로 안써주는 건가요?

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      dataCalc() 메서서드는 Page 클래스 하단에 위치해있습니다.

      삭제
    2. setCount에서 메서드를 호출해 줍니다.

      눈이 침침해 이 내용이 잘 안 보여서 문제가 뭔지.... 한참 찾았네요.

      비슷한 분께 도움이 되기를 바랍니다.

      삭제
  4. 안녕하세요 혹시 검색 기능도 구현해주실수 있을까요 ㅠㅠ
    옜날 버전과 지금 버전 페이징 기능도 달라서
    지금 버전 페이징에 옛날 버전 검색기능을 하면 안되는거 같더라구요 ㅠㅠ
    염치 없지만 부탁드립나다 ㅠ

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      이 게시물 태그.. 즉, '스프링 게시판 만들기'는 최종적으로 기존에 있던 기능을 모두 넣을 예정입니다.
      다만 제가 프로젝트를 계속 진행하다보니 점점 늦어지네요;;

      페이징 구현은 이걸로 끝이고, 검색 → 댓글 → 회원가입 순으로 진행할 예정입니다.

      삭제
  5. 익명5/21/2020

    따라하기만 하고 응용할 능력이 없는 초짜입니다 ㅠㅠ

    페이징 처음으로, 끝으로 보내는 건 어떻게 할지 혹시 올릴 예정은 없으신지...

    답글삭제
    답글
    1. 익명5/21/2020

      꾸역꾸역 하다보니 어떻게 비스므리하게는 됐네요. 여튼 이때껏 올려주신 거 모두 감사합니다 ㅎㅎ

      삭제
  6. 익명6/28/2020

    페이징처리 너무 어려웠는데 친절한 설명 감사드려요,,, 학원에서 배운거 복습하려니 까먹어서 막막했었는데ㅠ

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      제가 댓글 확인이 늦어서 죄송합니다..ㅠㅠ
      게시물의 내용은 페이징 처리 방식중 하나이고, 다른 방식의 처리 또는 라이브러리를 사용하는 방식도 있습니다.

      삭제
  7. 페이징할때마다 이해안되고 그냥 복사붙여넣기 형식이었는데
    덕분에 이해가 잘가네요 정말 감사합니다 :)

    답글삭제
  8. 익명5/03/2021

    사랑합니다 선생님..

    답글삭제
  9. 페이징 하는게 어렵게만 느껴졌는데.. 이렇게 친절하게 설명해주는 곳이 없어요 ㅠㅠ 잘 보고 배워갑니다. 감사합니다

    답글삭제
  10. 익명6/23/2021

    안녕하세요 페이징 구현을 따라하다보니 여기서 막혀서 질문드려요
    page가 0번만 출력되어 로그를 찍어보니 page0만 넘어오는데 어디서 문제가 된건지 알수 있을까요?

    답글삭제
    답글
    1. 익명6/23/2021

      메소드를 빼먹어서 문제가 되었네요 해결되었습니다. 감사합니다

      삭제
  11. 익명6/24/2021

    덕분에 도움이 많이 되었습니다. ^^ 좋은 글 올려주셔서 감사해요

    답글삭제
  12. 익명6/17/2022

    displayPost = (num - 1) * postNum; 이부분은 왜 이렇게 짜여진건가요? 이해가 안가서요 ㅠㅠ

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      이전 게시물인 #9. 페이징 구현 1에 있는 limit에 대한 설명과 쿼리문을 보시면 이해하기 쉽습니다.

      페이지 번호가 1인 경우, 1~10번째의 게시물이 보여져야하는데
      이때 limit 문을 사용하면 limit 0, 10 이 됩니다.

      마찬가지로 페이지 번호가 2인 경우, 11~20번째의 게시물이 보여져야하는데
      이때 limit 문을 사용하면 limit 10, 10 이 됩니다.

      limit [보여질 게시물 목록의 시작 인덱스], [한번에 출력할 갯수] 가 됩니다.
      (이렇게보니까 displayPost보다는 다른 네이밍이 더 알맞는거같네요-_-;)

      이때 '보여질 게시물 목록의 시작 인덱스'라는것은 일반적인 프로그래밍 언어 처럼, '첫번째'는 1이 아니라 0이라고 이해하셔도 됩니다.
      또는 지정된 숫자 다음번호 부터 읽어오는거라 이해하시면 됩니다.

      하지만 우리들이 흔히 말하는 첫번째는 숫자 1을 의미하는데..
      화면에 나오는 페이지 번호는 1이지만 개발자 기준에서는 0이 시작하는 번호이므로
      페이지 번호에서 1을 빼주는것으로 개발자가 쓰는 번호에 맞춘다, 라고 보시면 되겠습니다.


      그렇게 나온 인덱스에서 postNum을 곱하는건...
      1페이지일때는 개발용 시작번호로 맞추기 위해 -1해주면 0이 되고, 0 * 10을 하면 0이 됩니다.
      그럼 시작번호가 0이 되며, limit 0, 10이 됩니다.

      2페이지일때는 개발용 시작번호로 맞추기 위해 -1해주면 1이 되고 1*10을 하면 10이 됩니다.
      그럼 시작번호가 10이 되며, limit 10, 10이 됩니다.

      마지막으로 3페이지일때는 개발용 시작번호로 맞추기 위해 -1해주면 2이 되고 2*10을 하면 20이 됩니다.
      그럼 시작번호가 20이 되며, limit 20, 10이 됩니다.

      삭제
  13. 익명6/20/2022

    답변 감사합니다!!! 쿼리문 공부가 부족했네요 ㅠㅠ 너무 도움됐어요!

    답글삭제
  14. 와 이렇게도 따로 클래스를 둬서 할 수 있군요.. 엄청 나네요 ㄷㄷ 감동감동 ~ㅎㅎ

    답글삭제
  15. 익명8/22/2023

    갓벽하신 선생님 감사합니다!!!!!!!!!

    답글삭제