본문 바로가기
[ View ]/JSP

[ JSP ] 페이지 나누기

by 환이s 2023. 3. 22.

오늘은 게시판 만들 때 꼭 필요한 페이지 나누는 기능을 포스팅해보겠습니다!

 

 

■  페이지네이션

 

웹 사이트에는 게시판을 비롯한 여러 정보들을 페이지 단위로 보여줍니다.

따라서 원하는 자료가 있는 페이지를 바로가기하기 위해서는 페이지 하단에 위치한

 

[이전] 1 2 3 4 5 [다음]

 

과 같은 페이지 연결 링크가 필요합니다.

이를 페이지내비게이션 또는 페이지네이션(pagination)이라고 합니다.

 


■  페이지 나누기 공식

 

페이지를 나누려면 먼저 공식을 대입해야 합니다.

만약 전체 게시물 수 991개라면 페이지당 게시물 수를 10개로 표기하고자 할 때 몇 페이지를 표시해야 할까요?

99페이지가 아닌 100페이지를 표시해야 합니다.

 

991개의 페이지를 10으로 나누면 99.1입니다. 여기서 0.1도 1페이지이기 때문에 올림 처리 해주셔야 합니다.

 

계산 ) 991/10 => 99.1

 

나머지 공식들은 간단하게 아래에 정리했습니다.

 

페이지 시작번호, 끝번호 계산

 

1페이지 => 1 ~ 10 (게시물 번호 rownum으로 처리)
2페이지 => 11 ~ 20
. . .
11페이지 => 101 ~ 110
57페이지 => 561 ~ 570
99페이지 => 981 ~ 990
100페이지 => 991 ~ 1000

 

게시물의 시작번호=(현재페이지 - 1 ) * 페이지당 게시물수 + 1

 

1페이지 => (1-1) * 10 + 1 => 1
2페이지 => (2-1) * 10 + 1 => 11
7페이지 => (7-1) * 10 + 1 => 61

 

게시물의 끝번호=시작번호 + ( 페이지당 게시물수 – 1 )

 

1페이지 => 1 + ( 10 – 1 ) => 10
2페이지 => 11 + ( 10 – 1 ) => 20

 

전체 페이지 블록수

 

전체페이지개수 / 10

31 / 10 => 3.1 => 4개 블록

- 페이지 자체도 끊어서(블록처리) 표시해줘야 한다.

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

 

현재 페이지가 속한 블록

 

(현재페이지-1)/페이지블록단위 + 1

10페이지를 하나의 블록단위로 한다면
1페이지 => 몇 번째 블록? 1
(1-1)/10 + 1 =>1

9페이지 => 1블록
(9-1)/10 + 1 => 1블록

11페이지 => 2블록
(11-1)/10 + 1 =>2블록

27페이지
(27-1)/10 + 1 =>3블록

 

페이지 블록의 시작번호

 

(현재블록-1)*블록단위 + 1

1블록 => (1-1)*10 + 1 => 1
2블록 => (2-1)*10 + 1 => 11
3블록 => (3-1)*10 + 1 => 21

 

페이지 블록의 끝번호

 

블록시작번호+(페이지블록단위 - 1)

1블록 => 1+(10-1) => 10
2블록 => 11+(10-1) => 20
6블록 => 51+(10-1) => 60

■  응용 예제

 

페이지네이션을 알아보기 위해 먼저 사원 991개 데이터를 추가했습니다.

select*from test;

create table test as select *from emp where 1=0;

declare--선언부
i number := 1; --i변수에 1을 대입
begin -- 실행부
while i<=991 loop
insert into test(empno,ename) values
(i,'사원'||i);
i := i+1;
end loop;
end;
/

데이터를 추가하고 이제 페이지를 나눠야 하는데, 페이지를 나누려면 rownum 이 필요합니다. rownum은 연속된 범위의 값을 처리해 줍니다. 

 

그렇다고 rownum만 쓰면 안 되고, 서브 쿼리를 써야 합니다.

select *
from(
--전체 레코드에 일련번호 부여(2)
select A.*,rownum as rn
from(
    --전체 레코드를 뽑음(1)
    select empno, ename
    from test
    order by empno
    )A
)
where rn between 21 and 30;

위 코드는 서브쿼리를 사용해서 21페이지부터 30페이지를 출력하는 코드입니다.

출력된 결과를 보면 21번부터 30번까지 출력되는 걸 확인할 수 있는데, 그렇다면 SQL 문을 mapper 페이지에서 적용할 땐 between #{함수} and #{함수}를 넣어주면 됩니다.

 

SQL 작업이 끝났다면 COMMIT을 해줘야 합니다.

웹단에 데이터가 안 나올 수 있으니 꼭 해주고 넘어가야 합니다.

 

그럼 index 페이지부터 View 단 페이지까지 알아봅시다.

 

index page

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>index</title>

<script src="../include/jquery-3.6.3.min.js"></script>

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

<script type="text/javascript">

$(document).ready(function() {

list('1'); //1페이지란 뜻

});

function list(curPage) {

var param = "curPage="+curPage

$.ajax({

type:"post",

url:"${path}/page_servlet/list.do",

data:param,

success: function(result) {

$("#result").html(result);

}

});

}

</script>

</head>

<body>

<h2>페이지 나누기</h2>

<div id="result"></div>

</body>

</html>

 

DTO

package page;

public class EmpDTO {

private int empno;

private String ename;

 

//getter/setter , toString , 생성자

 

public int getEmpno() {

return empno;

}

public void setEmpno(int empno) {

this.empno = empno;

}

public String getEname() {

return ename;

}

public void setEname(String ename) {

this.ename = ename;

}

@Override

public String toString() {

return "EmpDTO [empno=" + empno + ", ename=" + ename + "]";

}

public EmpDTO(int empno, String ename) {

 

this.empno = empno;

this.ename = ename;

}

public EmpDTO() {

 

}

 

 

 

}

 

DTO 페이지까지 만들고 페이지네이션을 하려면 위에서 말씀드린 것처럼 페이지 처리 공식이 필요합니다.

공식 페이지는 아래 코드를 사용하셔도 됩니다.

 

Pager

package page;

public class Pager {

public static final int PAGE_SCALE=10;//페이지당 게시물수(10개)

public static final int BLOCK_SCALE=10;//페이지 블록 개수

private int curPage; //현재 페이지

private int prevPage; //이전 페이지

private int nextPage; //다음 페이지

private int totPage; //전체 페이지 개수

private int totBlock; //전체 페이지블록 개수

private int curBlock; //현재 페이지블록

private int prevBlock; //이전 페이지블록

private int nextBlock; //다음 페이지블록

private int pageBegin; // #{start}에 전달될 값

private int pageEnd; // #{end}에 전달될 값

private int blockStart; //페이지블록의 시작페이지 번호

private int blockEnd; //페이지블록의 끝페이지 번호

 

//getter, setter만 생성, 단 상수 2개는(PAGE_SCALE, BLOCK_SCALE) 빼고 만듦

 

// Pager(레코드개수, 보여줄 페이지번호)

public Pager(int count, int curPage){

curBlock=1; //페이지블록을 1로 초기화

this.curPage=curPage;

setTotPage(count); //전체 페이지 개수 계산

setPageRange(); // #{start}, #{end} 값 계산

setTotBlock(); // 페이지블록의 갯수 계산

setBlockRange(); //페이지블록의 범위 설정

}

//페이지블록의 범위 설정

public void setBlockRange(){

//현재페이지가 몇 번째 페이지블록에 속하는지 계산

//(현재페이지-1)/페이지블록단위 + 1

curBlock=(int)Math.ceil((curPage-1) / BLOCK_SCALE)+1;

//(현재블록-1)*블록단위+1

blockStart=(curBlock-1) * BLOCK_SCALE + 1;

//블록시작번호+(페이지블록단위-1)

blockEnd=blockStart + (BLOCK_SCALE-1);

//블록의 마지막 페이지번호가 범위를 초과하지 않도록 처리

if(blockEnd > totPage){

blockEnd = totPage;

}

//[이전] 11 12 13 14 15 16 17 18 19 20 [다음]

//[이전]을 눌렀을 때 이동할 페이지, 현재블록이 1이면 1로 감, [이전] 이란표시는 빼야 함

prevPage=curBlock==1 ? 1 : (curBlock-1) * BLOCK_SCALE;

//[다음]을 눌렀을 때 이동할 페이지

nextPage=curBlock>totBlock

? (curBlock*BLOCK_SCALE) : (curBlock*BLOCK_SCALE)+1;

if(nextPage >= totPage){//다음페이지가 토털페이지보다 크면 [다음] 표시 뺌

nextPage = totPage;

}

}

public void setPageRange(){

//시작번호=(현재페이지 - 1 ) * 페이지당 게시물수 + 1

pageBegin = (curPage - 1) * PAGE_SCALE + 1;

//끝번호=시작번호 + ( 페이지당 게시물수 – 1 )

pageEnd = pageBegin + (PAGE_SCALE - 1);

}

 

public int getCurPage() {

return curPage;

}

public void setCurPage(int curPage) {

this.curPage = curPage;

}

public int getPrevPage() {

return prevPage;

}

public void setPrevPage(int prevPage) {

this.prevPage = prevPage;

}

public int getNextPage() {

return nextPage;

}

public void setNextPage(int nextPage) {

this.nextPage = nextPage;

}

public int getTotPage() {

return totPage;

}

//전체 페이지 개수 계산

public void setTotPage(int count) {

// 991 / 10 => 99.1 올림 => 100

// 991/10 => 99

// 991.0/10 => 99.1

//ceil() 올림, round() 반올림, floor() 버림

totPage = (int)Math.ceil(count * 1.0 / PAGE_SCALE);

}

public int getTotBlock() {

return totBlock;

}

//페이지블록의 개수 계산

public void setTotBlock() {

// 991페이지 : 991/10 => 99, 한 페이지 누락이기 때문에 올림(ceil) 처리함

totBlock = (int)Math.ceil(totPage * 1.0 / BLOCK_SCALE);

}

public int getCurBlock() {

return curBlock;

}

public void setCurBlock(int curBlock) {

this.curBlock = curBlock;

}

public int getPrevBlock() {

return prevBlock;

}

public void setPrevBlock(int prevBlock) {

this.prevBlock = prevBlock;

}

public int getNextBlock() {

return nextBlock;

}

public void setNextBlock(int nextBlock) {

this.nextBlock = nextBlock;

}

public int getPageBegin() {

return pageBegin;

}

public void setPageBegin(int pageBegin) {

this.pageBegin = pageBegin;

}

public int getPageEnd() {

return pageEnd;

}

public void setPageEnd(int pageEnd) {

this.pageEnd = pageEnd;

}

public int getBlockStart() {

return blockStart;

}

public void setBlockStart(int blockStart) {

this.blockStart = blockStart;

}

public int getBlockEnd() {

return blockEnd;

}

public void setBlockEnd(int blockEnd) {

this.blockEnd = blockEnd;

}

 

 

}

 

Controller

package page;

import java.io.IOException;

import java.util.List;

import javax.servlet.RequestDispatcher;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

@WebServlet("/page_servlet/*")

public class PageController extends HttpServlet {

private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

String uri = request.getRequestURI();

EmpDAO dao = new EmpDAO();

 

if(uri.indexOf("list.do") != -1) {

int count = dao.empCount();//레코드 개수 계산

int curPage = 1; //null일 때는 기본값 1을 줌

if(request.getParameter("curPage") != null) {

curPage = Integer.parseInt(request.getParameter("curPage"));

}

Pager pager = new Pager(count, curPage);

 

int start=pager.getPageBegin();

int end =pager.getPageEnd();

 

List<EmpDTO> list = dao.empList(start,end);

request.setAttribute("list", list);

 

//페이지 내비게이션에 필요한 정보 전달

request.setAttribute("page", pager);

 

//포워딩

String page = "/page/list.jsp";

RequestDispatcher rd = request.getRequestDispatcher(page);

rd.forward(request, response);

}

 

 

}

 

protected void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

 

doGet(request, response);

}

}

 

DAO

package page;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import org.apache.ibatis.session.SqlSession;

import sqlmap.MybatisManager;

public class EmpDAO {

public int empCount() {

SqlSession session = MybatisManager.getInstance().openSession();

int count = session.selectOne("emp.empCount");

session.close();

return count;

}

 

 

public List<EmpDTO> empList(int start, int end){

SqlSession session = MybatisManager.getInstance().openSession();

 

Map<String, Object> map = new HashMap<>();

map.put("start", start);

map.put("end", end);

System.out.println(map);

 

 

List<EmpDTO> items = session.selectList("emp.empList" , map);

session.close();

return items;

}

}

DAO에서 mapper 파일로 id 값을 보냈습니다. 그럼 SQL문을 전달하려면 먼저 mapper 등록을 해야 합니다.

 

(이 부분은 Mybatis를 사용하는 부분이라서 궁금하신 개발자님들은 아래 페이지 참고해 보시면 도움이 될 거예요!)

 

 

[ JSP ] Mybatis 개념 및 설정

오늘은 SQL을 좀 더 쉽게 접근하게 해 주고 코드도 간결하게 만들어주는 Mybatis에 대해서 포스팅해 보겠습니다. ■ Mybatis란? Mybatis는 개발자가 지정한 SQL, 저장 프로시저를 지원하는 프레임워크(Jav

drg2524.tistory.com

 

mapper 등록

<mapper resource="/page/mapper/emp.xml"/>

 

mapper.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="emp">

<select id="empList" resultType="e">

select *

from(

select A.*,rownum as rn

from(

 

select empno, ename

from test

order by empno

)A

)

where rn between #{start} and #{end}

</select>

<select id="empCount" resultType="int">

select count(*) from test

</select>

</mapper>

위에서 언급한 것처럼 where 절에 해당 페이지를 빼고 함수를 추가합니다.

 

마지막으로 Controller에서 보내주는 View 단 페이지를 생성해서 결과를 확인해 봅시다.

 

View 단

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>list</title>

<script src="../include/jquery-3.6.3.min.js"></script>

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

<style type="text/css">

table {

width: 100%;

border: 1px solid black;

border-collapse: collapse;

}

th, td{

border: 1px solid black;

}

</style>

</head>

<body>

<table>

<tr>

<th>사번</th>

<th>이름</th>

</tr>

<c:forEach var = "row" items="${list}">

<tr>

<td>${row.empno}</td>

<td>${row.ename}</td>

</tr>

</c:forEach>

<tr>

<td colspan="2" align="center">

<!-- onclick시 index.jsp의 자동 호출되었던 메모리에 기억하고 있는 function list(curPage)를 호출한다. -->

<c:if test="${page.curPage > 1 }">

<a href="#" onclick="list('1')">[처음]</a>

</c:if>

<c:if test="${page.curBlock > 1 }">

<a href="#" onclick="list('${page.prevPage}')">[이전]</a>

</c:if>

<c:forEach var="num" begin="${page.blockStart}" end="${page.blockEnd}">

<c:choose>

<c:when test="${num == page.curPage}">

<span style="color: red">${num}</span>

</c:when>

<c:otherwise>

<a href="#" onclick="list('${num}')">${num}</a>

</c:otherwise>

</c:choose>

</c:forEach>

<c:if test="${page.curBlock < page.totBlock}">

<a href="#" onclick="list('${page.nextPage}')">[다음]</a>

</c:if>

<c:if test="${page.curPage < page.totPage}">

<a href="#" onclick="list('${page.totPage}')">[끝]</a>

</c:if>

 

</td>

 

</tr>

</table>

</body>

</html>

 


■  예제 출력 결과

 


마치며

 

오늘까지 페이지 처리하는 기능에 대해서 알아봤습니다.

그럼 실질적인 게시판을 만들어 봐야 하는데,

설렘 반 불안 반이네요...^^

728x90