[스프링SPRING] 게시판 페이징처리

게시판 페이징처리

  • 목적 : 필요한 만큼의 데이터를 전송/처리하기 위해서
  • 서버에서 처리 가능한 가장 빠른 속도로 페이지 처리를 해야한다.
  • 방식 2가지
    • <a>태그 : href속성으로 이동할 uri 지정
      • 장점 : 검색엔진 노출에 효과적이다. 한번에 정보를 파악하기가 쉽다.
      • 한계 : 반복적인 링크정보가 필요
    • <form>태그 : action속성으로 추가적인 정보를 가지고 지정한 uri러 이동
      • 장점 : 간결한 페이지 이동, 필요한 데이터 추가저장 가능
  • 페이징처리의 원칙
    • 페이징처리는 반드시 get방식이어야 한다.
      • 사용자에게 url로 정보를 전달하기때문에 get방식으로 해야한다.
    • 페이징처리 완료시 반드시 목록으로 가기동작이 있어야 한다.
    • 만약 3페이지의 글을 읽고 다시 목록으로 돌아갈때, 다시 3페이지로 이동해야한다.
    • 페이징 처리시 반드시 필요한 페이지 번호만 제공
    • 페이징 처리의 크기에 따라서 [이전],[다음]동작을 구현
  • 페이징처리 개발순서
    • 페이지 데이터가 화면에 출력
    • 화면 하단에 보이는 페이징처리
    • 본문 -> 리스트이동시 페이징처리
  • 페이지 하단부 페이징처리와 사용객체
    • 페이지 이동시 처리
      • Criteria 객체안의 page와 pageSize를 사용
      • page - 현재 페이지번호
      • pageSize - 한 페이지에 출력할 개수
    • 직접 계산해서 처리
      • 시작페이지번호(startPage)
      • 예) 화면에 10개의 페이지번호 데이터를 출력.
      • [이전]이 부분 페이지 이동 제어를 담당.
      • 끝페이지번호(endPage) : 시작페이지 번호에서 몇개 보여줄건지 결정
        • 예) 총65페이지를 10개씩 출력하려면 총 7개페이지가 있어야한다.
    • DB에서 처리
      • 전체 글의 수(totalCount)
        • DB에서 row수로 가져오거나 ai제약조건있으면 가장 마지막 번호 가져오기
      • [이전]페이지이동(prev) : 시작페이지가 1이 아닌 경우 생성됨
      • [다음]페이지이동(next) : 끝페이지 페이지블럭




BoardController.java 코드추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//페이징처리한 글목록
@RequestMapping(value = "/listCri", method = RequestMethod.GET)
public void listCriGET(Criteria cri, Model model) throws Exception{
l.info("C: listCri 겟 호출" + cri);
model.addAttribute("boardList", service.listCri(cri));
}

//글목록보기(PageMaker객체 사용)
// http://localhost:8088/board/listPage
@RequestMapping(value = "/listPage", method = RequestMethod.GET)
public void listPageGET(Criteria cri, Model model) throws Exception{
l.info("C: cri는 "+cri);
model.addAttribute("boardList", service.listCri(cri));

PageMaker pm = new PageMaker();
pm.setCri(cri);
pm.setTotalCount(service.pageCount()); //DB의 전체ROW수 입력

// 뷰페이지로 전달
model.addAttribute("pm", pm);
}




BoardService.java 코드추가

1
2
3
4
5
//페이징처리한 글목록
public List<BoardVO> listCri(Criteria cri) throws Exception;

//DB 테이블에 있는 모든 글 개수 계산 후 리턴
public int pageCount() throws Exception;




BoardServiceImpl.java 코드추가

1
2
3
4
5
6
7
8
9
10
11
//페이지처리한 글 목록
@Override
public List<BoardVO> listCri(Criteria cri) throws Exception {
return bdao.listPageCri(cri);
}

//DB 테이블에 있는 모든 글 개수 계산 후 리턴
@Override
public int pageCount() throws Exception {
return bdao.pageCount();
}




BoardDAO.java 코드추가

1
2
3
4
5
6
7
8
//특정 페이지에 있는 글정보를 확인 
public List<BoardVO> listPage(int page) throws Exception;

//페이징 처리하는 동작(Criteria 객체 사용)
public List<BoardVO> listPageCri(Criteria cri) throws Exception;

//DB 테이블에 있는 모든 글 개수 계산 후 리턴
public int pageCount() throws Exception;




BoardDAOImpl.java 코드추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//페이징
@Override
public List<BoardVO> listPage(int page) throws Exception {
//페이지가 0인 경우 1로 바꿔서 처리
if(page <= 0) {
page = 1;
}
page = (page - 1)*10;
return session.selectList(namespace+".listPage", page);
}

//페이징 처리하는 동작(Criteria 객체 사용)
@Override
public List<BoardVO> listPageCri(Criteria cri) throws Exception {
System.out.println("DAO: listPageCri 호출");
return session.selectList(namespace+".listPageCri", cri);
}

//DB 테이블에 있는 모든 글 개수 계산 후 리턴
@Override
public int pageCount() throws Exception{
return session.selectOne(namespace+".pageCount");
}




BoardMapper.xml 코드추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 페이지정보를 10개씩 가져오기 -->
<select id="listPage" resultType="BoardVO">
select *
from tbl_board
order by bno desc, regdate desc
limit #{page},10
</select>

<!-- 페이지정보를 지정한 크기만큼 가져오기 -->
<select id="listPageCri" resultType="BoardVO">
select *
from tbl_board
order by bno desc, regdate desc
limit #{pageStart},#{pageSize}
</select>

<select id="pageCount" resultType="int">
select count(*) from tbl_board
</select>




BoardDAOTest.java 테스트 코드 추가하기

  • src/test/java 폴더에서 test파일을 생성한다.
  • 테스트가 정상적으로 작동함을 콘솔에서 확인할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/root-context.xml"})
public class BoardDAOTest {

//DB객체
@Inject
private BoardDAO bdao;

// 특정 페이지의 글 정보 확인
@Test
public void testListPage() throws Exception{
//페이지정보 1,3,5
int page = 5;
//주입받은 DAO 객체 사용 접근
List<BoardVO> boardList = bdao.listPage(page);
//리스트에 저장된 정보를 콘솔창에 출력
for(BoardVO vo :boardList) {
System.out.println(vo.getBno()+"------"+vo.getTitle());
}
}
}




Criteria.java 생성

  • src/main/java에 domain폴더 하위에 생성한다.
  • setter메서드에 추가 제어 코드를 넣어준다.
  • 멤버변수가 아니지만 gettet와 setter를 생성하면 Mapper에서 접근가능하다.
    • 멤버변수는 없지만 DB에서 처리해야하는 값을 생성하기 위해 GET메서드를 생성한다.
    • Mapper에 접근가능한 이유는 getXXXX() 형태를 Mapper가 가지고 있기 때문
    • Mapper에서 사용하는 방법은 #{get을_제외한_메서드명}
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Criteria {
//페이지 정보, 페이지 크기 정보를 저장하는 객체
private int page;
private int pageSize;

public Criteria() {
this.page = 1;
this.pageSize = 10;
}

public int getPage() {
return page;
}

public void setPage(int page) {
if(page <=0) {
this.page = 1;
return;
}
this.page = page;
}

public int getPageSize() {
return pageSize;
}

public void setPageSize(int pageSize) {
if(pageSize <=0 || pageSize > 100) {
this.pageSize = 10;
return;
}
this.pageSize = pageSize;
}

//페이징처리 시작하는 값생성
//멤버변수가 아니지만 gettet와 setter를 생성하면 Mapper에서 접근가능하다.
//Mapper에서 사용하는 법 #{pageStart}
public int getPageStart() {
return (this.page - 1)*this.pageSize;
}

//toString()
@Override
public String toString() {
return "Criteria [page=" + page + ", pageSize=" + pageSize + "]";
}
}




PageMaker.java 객체 생성

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package com.itwillbs.domain;

public class PageMaker {
private int totalCount;
private int startPage;
private int endPage;
private boolean prev;
private boolean next;

//Criteria안에 page, pageSize 있음
private Criteria cri;

//화면에 보여지는 페이지블럭의 수
private int displayPageNum = 10;

public PageMaker() {}
public PageMaker(int totalCount, int startPage, int endPage, boolean prev, boolean next, Criteria cri,
int displayPageNum) {
super();
this.totalCount = totalCount;
this.startPage = startPage;
this.endPage = endPage;
this.prev = prev;
this.next = next;
this.cri = cri;
this.displayPageNum = displayPageNum;
}

public int getTotalCount() {
return totalCount;
}

public void setTotalCount(int totalCount) { //변경
this.totalCount = totalCount;
System.out.println("DB에서 총 글의 개수를 계산");
//총 글의 개수를 가지고 왔을때 필요한 정보를 계산
calcDate();
}

private void calcDate() {
endPage = (int) (Math.ceil(cri.getPage()/(double)displayPageNum) * displayPageNum);
startPage = (endPage - displayPageNum) +1;
int tempEndPage = (int) (Math.ceil(totalCount/(double)cri.getPageSize()));
if(endPage > tempEndPage) endPage = tempEndPage;
prev = (startPage == 1? false:true);
next = (endPage * cri.getPageSize() >= totalCount? false:true);
System.out.println("페이징처리정보 계산");
}

public int getStartPage() {
return startPage;
}

public void setStartPage(int startPage) {
this.startPage = startPage;
}

public int getEndPage() {
return endPage;
}

public void setEndPage(int endPage) {
this.endPage = endPage;
}

public boolean isPrev() {
return prev;
}

public void setPrev(boolean prev) {
this.prev = prev;
}

public boolean isNext() {
return next;
}

public void setNext(boolean next) {
this.next = next;
}

public Criteria getCri() {
return cri;
}

public void setCri(Criteria cri) { //변경
this.cri = cri;
}

public int getDisplayPageNum() {
return displayPageNum;
}

public void setDisplayPageNum(int displayPageNum) {
this.displayPageNum = displayPageNum;
}

@Override
public String toString() {
return "PageMaker [totalCount=" + totalCount + ", startPage=" + startPage + ", endPage=" + endPage + ", prev="
+ prev + ", next=" + next + ", cri=" + cri + ", displayPageNum=" + displayPageNum + "]";
}
}




listPage.jsp 뷰 생성

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ include file="../include/header.jsp" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<!-- Main content -->
<section class="content">
<div class="row">
<!-- left column -->
<div class="col-md-12">
<!-- general form elements -->
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">게시판 글 조회</h3>
</div>
<!-- /.box-header -->

<!-- 바디 -->
<div class="box-body">
<table class="table table-bodered">
<tr>
<th>번호</th>
<th>글쓴이</th>
<th>제목</th>
<th>내용</th>
<th>작성일</th>
<th>조회수</th>
</tr>
<c:forEach var="i" items="${boardList }">
<tr>
<td>${i.bno }</td>
<td>${i.writer }</td>
<td><a href="/board/read?bno=${i.bno }">${i.title }</a></td>
<td>${i.content }</td>
<td><fmt:formatDate value="${i.regdate }" pattern="yyyy-MM-dd (E) HH:mm" /></td>
<td><span class="badge bg-red">${i.viewcnt }</span></td>
</tr>
</c:forEach>
</table>
</div>

<div class="box-footer">
<div class="text-center">
<ul class="pagination">
<!-- 이전prev -->
<c:if test="${pm.prev }">
<li><a href="listPage?page=${pm.startPage-1}">&laquo;</a></li>
</c:if>
<!-- 페이지블럭 -->
<c:forEach var="idx" begin="${pm.startPage }" end="${pm.endPage }">
<!-- 삼항연산자를 사용해서 class로 스타일적용 -->
<li ${pm.cri.page == idx? 'class=active':''}>
<a href="listPage?page=${idx }">${idx}</a>
</li>
</c:forEach>
<!-- 다음next -->
<c:if test="${pm.next && pm.endPage > 0}">
<li><a href="listPage?page=${pm.endPage+1}">&raquo;</a></li>
</c:if>
</ul>
</div>
</div>

<!-- 바디 끝 -->
</div>
<!-- /.box -->
</div>
<!--/.col (left) -->
</div>
<!-- /.row -->
</section>
<!-- /.content -->
</div>
<!-- /.content-wrapper -->

<script type="text/javascript">
let result="${result}";
let isResist = "${isRegist}";

if(result == 'success' && isResist == 'true'){
alert('성공적으로 글 작성되었습니다.');
}else if(result == 'success' && isResist != 'true'){
alert('글쓰기가 실패하였습니다.');
}else if(result =='up-ok'){
alert('성공적으로 글이 수정되었습니다.');
}else if(result =='delete-ok'){
alert('성공적으로 글이 삭제되었습니다.');
}else{
//글쓰기없이 출력시 alert창 필요없음
}
</script>

<%@ include file="../include/footer.jsp" %>




결과