본문 바로가기
[ JAVA ]/JAVA Spring

[ Spring ] 게시판 02 - 회원 목록 페이지 나누기 기능 구현

by 환이s 2023. 4. 26.


프로젝트 구조

 

 

 

 

오늘은 이전 포스팅에서 생성했던 유저 목록 페이지에 페이지 나누기 기능을 추가해 보겠습니다.

 

페이지를 나누려면 별도로 페이지네이션 기능 공식 파일을 생성해서 변수로 추출해서 사용해야 하는데,

이전에 JSP 포스팅했을 때 사용했던 Pager.java 파일을 사용해서 기능 구현을 해보겠습니다.


Pager.java

 

Pager 파일은 페이지 나누는 공식과 변수를 담은 파일입니다. 각 변수의 역할은 주석으로 작성했습니다.

 

package com.example.spring.service.board;

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;
	}
	
	
}

BoardController

 

Controller 에서는 이전 포스팅에서 생성했던 list.do url 메서드에 Pager 파일을 대입해서 stard,end 설정 후  Service에 같이 보내줍니다.

 

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.example.spring.model.board.dto.BoardDTO;
import com.example.spring.service.board.BoardService;
import com.example.spring.service.board.Pager;

@Controller
@RequestMapping("board/*")
public class BoardController {

	@Inject
	BoardService boardService;
	
	//로깅을 위한 변수
	private static final Logger logger=
			LoggerFactory.getLogger(BoardController.class);
	
	@RequestMapping("list.do") // 세부 url
	//defaultValue = "1" : 파라미터가 없을 때 1로 셋팅해줌
	public ModelAndView list(@RequestParam(defaultValue = "1") int curPage) throws Exception{
		//레코드 개수 계산
		int count = boardService.countArticle();
		// 페이지 관련 설정
		Pager pager = new Pager(count, curPage);
		int start=pager.getPageBegin();
		int end =pager.getPageEnd();
		
		List<BoardDTO> list=boardService.listAll(start,end); // 게시물 목록
		logger.info(list.toString());
		
		ModelAndView mav = new ModelAndView();
		
		Map<String, Object> map = new HashMap<>();
		map.put("list", list); // map에 자료 저장
		map.put("count",count); //레코드 갯수 파악
		map.put("pager", pager); // 페이지 네비게이션을 위한 변수
		mav.setViewName("board/list"); // 포워딩 뷰
		mav.addObject("map",map); //전달 데이터
		return mav;
	}

 

페이지 나누기 기능은 mapper 에서 쿼리문으로 요청을 보내서 데이터만 가져오면 되기 때문에 Service단과 DAO 에서는 각각 Service  => DAO 요청 , DAO => mapper 요청을 보내는 코드만 작성해 줍니다.

 


BoardService 

 

package com.example.spring.service.board;

import java.util.List;

import javax.inject.Inject;
import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.spring.model.board.dao.BoardDAO;
import com.example.spring.model.board.dto.BoardDTO;

@Service
public class BoardServiceImpl implements BoardService {
	
	@Inject
	BoardDAO boardDao;	
    
    
    @Override //레코드 개수 
	public int countArticle() throws Exception {
		return boardDao.countArticle();
	}

	@Override //유저 목록 
	public List<BoardDTO> listAll(int start, int end) throws Exception {
		return boardDao.listAll(start,end); // 페이지네이션 변수 보내줌
	}

BoardDAO

 

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.servlet.http.HttpSession;
import javax.sound.midi.Sequence;

import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;

import com.example.spring.model.board.dto.BoardDTO;

@Repository
public class BoardDAOImpl implements BoardDAO {

	@Override
	public List<BoardDTO> listAll(int start, int end) throws Exception {
    	// mapper에 요청할 땐 매개 변수 1개만 보낼 수 있기 때문에
        // map에 담아서 보내줌
		Map<String, Object> map = new HashMap<>();
		map.put("start", start);
		map.put("end", end);
		return sqlsession.selectList("board.listAll" , map) ;
	}
    
    	@Override
	public int countArticle() throws Exception {
		
		return sqlsession.selectOne("board.countArticle");
	}

 

그럼 다음으로 쿼리문을 작성해서 데이터를 디비에서 가져옵니다.


mapper

 

목록에 페이지 나누기 기능을 구현할 땐 rownum을 활용하면 됩니다.

또한, 유저 ID 값과 NAME이 출력되어야 하기 때문에 member 테이블과 조인 시켜서 쿼리문을 작성해 줍니다.

 

<?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와 중복되지 않도록 네임스페이스 기재 -->
<mapper namespace="board">
	<select id="listAll" resultType="com.example.spring.model.board.dto.BoardDTO">
	select *
	from ( 
	    select rownum as rn, A.*
	    from(
	    select bno,title,writer,name,regdate,viewcnt
	    from board b, member m
	    where b.writer = m.userid
	    order by bno desc
	)A
	)where rn between #{start} and #{end}
	</select>
    
    	<!-- 레코드 갯수 계산  -->
	<select id="countArticle" resultType="int" >
	select count(*)
	from board b , member m
	where b.writer=m.userid
	</select>

list.jsp

 

이전 포스팅에서 생성했던 목록 페이지에서 맨 아래줄에 페이지네이션 코드를 변수값을 대입해서 작성합니다.

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<%@ include file="../include/header.jsp" %>
<script type="text/javascript">
$(function() {
	$("#btnWrite").click(function(){
		location.href="${path}/board/write.do";
	});
});

function list(page) {
	location.href="${path}/board/list.do?curPage="+page;
}

</script>
</head>
<body>
<%@ include file="../include/menu.jsp" %>
<h2>게시판</h2>
<button type="button" id="btnWrite">글쓰기</button>
${map.count}개의 게시물이 있습니다.
<table border="1">
  <tr>
    <th>번호</th>
    <th>제목</th>
    <th>이름</th>
    <th>내용</th>
    <th>날짜</th>
    <th>조회수</th>
  </tr>
 <c:forEach var="row" items="${map.list}">
  <tr>
    <td>${row.bno}</td>
    <td><a href="${path }/board/view.do?bno=${row.bno}">${row.title}</a></td>
    <td>${row.name}</td>
    <td>${row.content}</td>
    <td><fmt:formatDate value="${row.regdate}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
    <td>${row.viewcnt}</td>
  </tr>
 </c:forEach>
 <!-- 페이지 네비게이션 출력 -->
	<tr>
		<td colspan="6" align="center">
			<c:if test="${map.pager.curBlock > 1}">
				<a href="#" onclick="list('1')">[처음]</a>
			</c:if>
			<c:if test="${map.pager.curBlock > 1}">
				<a href="#" onclick="list('${map.pager.prevPage}')">
				[이전]</a>
			</c:if>
			<c:forEach var="num" 
				begin="${map.pager.blockStart}"
				end="${map.pager.blockEnd}">
				<c:choose>
					<c:when test="${num == map.pager.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="${map.pager.curBlock < map.pager.totBlock}">
				<a href="#" 
				onclick="list('${map.pager.nextPage}')">[다음]</a>
			</c:if>
			<c:if test="${map.pager.curPage < map.pager.totPage}">
				<a href="#" 
				onclick="list('${map.pager.totPage}')">[끝]</a>
			</c:if>
		</td>
	</tr>
 
 
</table>
</body>
</html>

출력 

 


마치며

 

오늘은 유저 목록 페이지 나누기 기능을 구현해 보았습니다.

다음 포스팅에서 뵙겠습니다.

 

728x90