[ JAVA ]/JAVA Spring

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

환이s 2023. 4. 26. 15:23
728x90


프로젝트 구조

 

 

 

 

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

 

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

이전에 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