본문 바로가기
Java/Spring

Spring) 페이징 처리 (content, pagebar 영역) - RowBounds

by 박채니 2022. 8. 30.
안녕하세요, 코린이의 코딩 학습기 채니 입니다.
개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.

 

 

BoardList 조회

 

DB 구조

-- 게시판 | 첨부파일 테이블 생성
create table board(
    no number,
    title varchar2(1000) not null,
    member_id varchar2(15),
    content varchar2(4000),
    read_count number default 0,
    created_at date default sysdate,
    updated_at date,
    constraint pk_board_no primary key(no),
    constraint fk_board_member_id foreign key(member_id) references member(member_id) on delete set null
);

create sequence seq_board_no;

create table attachment(
    no number,
    board_no number not null,
    original_filename varchar2(256) not null,
    renamed_filename varchar2(256) not null,
    download_count number default 0,
    created_at date default sysdate,
    constraint pk_attachment_no primary key(no),
    constraint fk_attachment_board_no foreign key (board_no) references board(no) on delete cascade
);

create sequence seq_attachment_no;

 

Dto

BoardEntity

// DB랑 구조가 같은 클래스
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BoardEntity {
	private int no;
	private String title;
	private String memberId;
	private String content;
	private int readCount;
	private LocalDateTime createdAt;
	private LocalDateTime updatedAt;
}

 

Board

@Data
@NoArgsConstructor
@ToString(callSuper = true)
public class Board extends BoardEntity {
	private int attachCount;

	public Board(int no, String title, String memberId, String content, int readCount, LocalDateTime createdAt,
			LocalDateTime updatedAt, int attachCount) {
		super(no, title, memberId, content, readCount, createdAt, updatedAt);
		this.attachCount = attachCount;
	}
}

Lombok의 @AllArgsConstructor 어노테이션을 사용하면 상속받고 있는 부모(super)필드까지 포함된 생성자를 만들어 주진 않습니다.

@NoArgsConstructor

@AllArgsConstructor 어노테이션 사용 시

이처럼 해당 클래스의 필드만을 인식하여 생성자를 만들어주므로, 파라미터 생성자는 lombok 대신 별도로 생성해주었습니다.

@Data 어노테이션 내의 ToString 또한 부모의 ToString을 호출해주지 않기 때문에 @ToString 어노테이션의 callsuper속성을 이용해 부모의 tostring도 호출되도록 하였습니다.

 

Controller

BoardController

@Controller
@Slf4j
@RequestMapping("/board")
public class BoardController {
	@Autowired
	private BoardService boardService;
	
	@RequestMapping("/boardList.do")
	public void boardList(Model model) {
		List<Board> list = boardService.selectAll();
		log.debug("list = {}", list);
		model.addAttribute("list", list);
	}
}

 

Service

BoardService interface 생략

BoardServiceImpl

@Service
public class BoardServiceImpl implements BoardService {
	@Autowired
	private BoardDao boardDao;
	
	@Override
	public List<Board> selectAll() {
		return boardDao.selectAll();
	}
}

 

Dao

BoardDao interface

@Mapper
public interface BoardDao {
	
	@Select("select b.*, (select count(*) from attachment where board_no = b.no) attach_count from board b order by no desc")
	List<Board> selectAll();
	
}

 

boardList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<jsp:include page="/WEB-INF/views/common/header.jsp">
	<jsp:param value="게시판" name="title"/>
</jsp:include>
<style>
/*글쓰기버튼*/
input#btn-add{float:right; margin: 0 0 15px;}
</style>
<section id="board-container" class="container">
	<input type="button" value="글쓰기" id="btn-add" class="btn btn-outline-success"/>
	<table id="tbl-board" class="table table-striped table-hover">
		<tr>
			<th>번호</th>
			<th>제목</th>
			<th>작성자</th>
			<th>작성일</th>
			<th>첨부파일</th> <!-- 첨부파일 있을 경우, /resources/images/file.png 표시 width: 16px-->
			<th>조회수</th>
		</tr>
		<c:if test="${not empty list}">
			<c:forEach items="${list}" var="list">
				<tr>
					<td>${list.no}</td>
					<td>${list.title}</td>
					<td>${list.memberId}</td>
					<td>
						<fmt:parseDate value="${list.createdAt}" var="createdAt" pattern="yyyy-MM-dd'T'HH:mm" />
						<fmt:formatDate value="${createdAt}" pattern="yy/MM/dd HH:mm"/>
					</td>
					<td>
						<c:if test="${list.attachCount gt 0}">
							<img src="${pageContext.request.contextPath}/resources/images/file.png" width="16px"/>
						</c:if>
					</td>
					<td>${list.readCount}</td>
				</tr>
			</c:forEach>
		</c:if>
		<c:if test="${empty list}">
			<tr>
				<td colspan="6" class="text-center">조회된 게시글이 없습니다.</td>
			</tr>
		</c:if>
	</table>
</section> 

<jsp:include page="/WEB-INF/views/common/footer.jsp"></jsp:include>

 


페이징 처리

Mybatis에서 제공하는 페이징객체처리 RowBounds 이용! (limit, offset)

 

cPage - 현재 페이지

limit - 한 페이지에서 보여줄 개수

offset - 건너뛸 개수

 

Controller

BoardController

@RequestMapping("/boardList.do")
public void boardList(@RequestParam(defaultValue = "1") int cPage, Model model) {
    Map<String, Integer> param = new HashMap<>();
    param.put("cPage", cPage);
    param.put("limit", 10);

    List<Board> list = boardService.selectAll(param);
    log.debug("list = {}", list);
    model.addAttribute("list", list);
}

현재 페이지를 cPage로 받아 처리하며, 게시판 페이지에 처음 올 경우 cPage가 없기 때문에 defaultValue를 "1"로 지정해주었습니다.

 

Service

BoardService interface 생략

BoardServiceImpl

@Override
public List<Board> selectAll(Map<String, Integer> param) {
    // mybatis에서 제공하는 페이징처리객체 RowBounds
    // offset, limit
    int limit = param.get("limit"); // 한 페이지에서 보여줄 개수
    int offset = (param.get("cPage") - 1) * limit; // 건너뛸 개수
    RowBounds rowBounds = new RowBounds(offset, limit);

    return boardDao.selectAll(rowBounds);
}

cPage = 1 ? offset = (1-1) * 10, 즉 offset은 0이므로, 건너뛰는 개수가 없습니다.

cPage = 2 ? offset = (2-1) * 10, 즉 offset은 10이므로, 10개의 게시글을 건너뛰어 11~20개의 게시글을 랜더링

cPage = 3 ? offset = (3-1) * 10, 즉 offset은 20이므로, 20개의 게시글을 건너뛰어 21~30개의 게시글을 랜더링

...

...

이와 같은 구조를 가지고 있어 쉽게 페이징 처리를 할 수 있습니다.

 

Dao

BoardDao interface 생략

@Mapper
public interface BoardDao {
	
	@Select("select b.*, (select count(*) from attachment where board_no = b.no) attach_count from board b order by no desc")
	List<Board> selectAll(RowBounds rowBounds);
	
}

페이징 처리에 따라서 랜더링되는 게시글이 다른 것을 확인할 수 있습니다.

 


페이지 바 처리

 

Controller

BoardController

@RequestMapping("/boardList.do")
public void boardList(@RequestParam(defaultValue = "1") int cPage, Model model, HttpServletRequest request) {
    // 1. content 영역
    Map<String, Integer> param = new HashMap<>();
    int limit = 10;
    param.put("cPage", cPage);
    param.put("limit", 10);

    List<Board> list = boardService.selectAll(param);
    log.debug("list = {}", list);
    model.addAttribute("list", list);

    // 2. pagebar 영역
    int totalContent = boardService.getTotalContent();
    String url = request.getRequestURI();
    String pagebar = HelloSpringUtils.getPagebar(cPage, limit, totalContent, url);
    log.debug(pagebar);
    model.addAttribute("pagebar", pagebar);
}

 

HelloSpringUtils

getPagebar

public class HelloSpringUtils {
	/**
	 * totalPage 전체페이지수
	 * pagebarSize 
	 * pageNo
	 * pagebarStart
	 * pagebarEnd
	 */
	public static String getPagebar(int cPage, int limit, int totalContent, String url) {
		StringBuffer pagebar = new StringBuffer();
		url += "?cPage=";
		
		final int pagebarSize = 5;
		final int totalPage = (int)Math.ceil((double)totalContent / limit);
		final int pagebarStart = ((cPage - 1) / pagebarSize) * pagebarSize + 1;
		final int pagebarEnd = pagebarStart + pagebarSize - 1;
		int pageNo = pagebarStart;
		
		pagebar.append("<ul class=\"pagination justify-content-center\">\n");
		// 1. previous
		if(cPage == 1) {
			pagebar.append("<li class=\"page-item disabled\">\n"
					+ "      <a class=\"page-link\" href=\"#\" aria-label=\"Previous\">\n"
					+ "        <span aria-hidden=\"true\">&laquo;</span>\n"
					+ "        <span class=\"sr-only\">Previous</span>\n"
					+ "      </a>\n"
					+ "    </li>\n");
		} else {
			pagebar.append("<li class=\"page-item\">\n"
					+ "      <a class=\"page-link\" href=\"" + url + (pageNo-1) + "\" aria-label=\"Previous\">\n"
					+ "        <span aria-hidden=\"true\">&laquo;</span>\n"
					+ "        <span class=\"sr-only\">Previous</span>\n"
					+ "      </a>\n"
					+ "    </li>\n");
		}
		
		// 2. pageNo
		while(pageNo <= pagebarEnd && pageNo <= totalPage) {
			if(pageNo == cPage) {
				pagebar.append("<li class=\"page-item active\"><a class=\"page-link\" href=\"#\">" + pageNo + "</a></li>\n");
			} else {				
				pagebar.append("<li class=\"page-item\"><a class=\"page-link\" href=\"" + url + pageNo +"\">" + pageNo + "</a></li>\n");
			}
			pageNo++;
		}
		
		// 3. next
		if(pageNo > totalPage) {
			pagebar.append("<li class=\"page-item disabled\">\n"
					+ "      <a class=\"page-link\" href=\"#\" aria-label=\"Next\">\n"
					+ "        <span aria-hidden=\"true\">&raquo;</span>\n"
					+ "        <span class=\"sr-only\">Next</span>\n"
					+ "      </a>\r\n"
					+ "    </li>\n");
		} else {
			pagebar.append("<li class=\"page-item\">\n"
					+ "      <a class=\"page-link\" href=\"" + url + pageNo + "\" aria-label=\"Next\">\n"
					+ "        <span aria-hidden=\"true\">&raquo;</span>\n"
					+ "        <span class=\"sr-only\">Next</span>\n"
					+ "      </a>\n"
					+ "    </li>\n");
		}
		pagebar.append("</ul>");
		
		return pagebar.toString();
	}
}

※ 페이지 바 처리 관련 자세한 설명은 아래 포스팅 참고

https://chanychu.tistory.com/322

 

JSP) 기본 페이징 처리

안녕하세요, 코린이의 코딩 학습기 채니 입니다. 개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다. 페이징 페이지에 보여지는 목록이 너무 많다면, 한 눈에 보기가 어렵고 스

chanychu.tistory.com