페이징(paging)은 데이터베이스에서 대량의 데이터를 효율적으로 표시하기 위해 아래와 같이여러 페이지로 나누는 것이다.
1. 페이징의 기본 원리
- 총 데이터 수 (Total Record Count): 데이터베이스에서 조회할 수 있는 전체 데이터의 수
- 페이지 크기 (Record Size): 한 페이지에 표시할 데이터(인스턴스)의 수
- 현재 페이지 번호 (Current Page): 사용자가 현재 보고 있는 페이지의 번호
- 전체 페이지 수 (Total Page Count): 위 사진에서 총 8페이지가 있는 것과 같다.
2. 코드에서의 페이징 구현
페이징은 Pagination 클래스와 SearchVo 클래스에서 처리된다.
2.1. Pagination 클래스
- startPage: 현재 페이지 그룹의 시작 페이지 번호
- endPage: 현재 페이지 그룹의 끝 페이지 번호
- imitStart: 데이터베이스 쿼리에서 사용할 LIMIT 시작 위치
- existPrevPage: 이전 페이지의 존재 여부
- existNextPage: 다음 페이지의 존재 여부
Pagination 클래스의 생성자는 totalRecordCount와 SearchVo 객체를 받아 페이징 정보를 계산한다.
private void calculation(SearchVo params) {
// 전체 페이지 수 계산
totalPageCount = (int) Math.ceil((double) totalRecordCount / (double) params.getRecordSize());
// 현재 페이지 번호가 전체 페이지 수보다 큰 경우, 현재 페이지 번호에 전체 페이지 수 저장
if (params.getPage() > totalPageCount) {
params.setPage(totalPageCount);
}
// 첫 페이지 번호 계산
startPage = ((params.getPage() - 1) / params.getPageSize()) * params.getPageSize() + 1;
// 끝 페이지 번호 계산
endPage = startPage + params.getPageSize() - 1;
// 끝 페이지가 전체 페이지 수보다 큰 경우, 끝 페이지 전체 페이지 수 저장
if (endPage > totalPageCount) {
endPage = totalPageCount;
}
// LIMIT 시작 위치 계산
limitStart = (params.getPage() - 1) * params.getRecordSize();
// 이전 페이지 존재 여부 확인
existPrevPage = startPage != 1;
// 다음 페이지 존재 여부 확인
existNextPage = (endPage * params.getRecordSize()) < totalRecordCount;
}
- 총 페이지 수 계산: 전체 데이터 수를 페이지 크기로 나누어 전체 페이지 수를 계산한다.
- 페이지 번호 보정: 사용자가 요청한 페이지 번호가 전체 페이지 수를 초과할 경우 현재 페이지를 전체 페이지로 수정하고, 끝 페이지가 전체 페이지를 초과할 경우 끝 페이지를 전체 페이지로 정한다.
- 페이지 범위 설정: 현재 페이지에 따라 시작 페이지 및 끝 페이지를 설정한다.
- LIMIT 시작 위치 계산: 데이터베이스 쿼리에서 사용할 OFFSET 값을 계산한다.
(LIMIT은 가져올 데이터의 양, OFFSET은 가져올 데이터의 시작위치이다.)
2.2. SearchVo 클래스
- page: 현재 페이지 번호
- recordSize: 페이지당 출력할 데이터 개수
- pageSize: 화면 하단에 출력할 페이지 사이즈
- pagination: 페이징 정보를 담고 있는 Pagination 객체
public int getOffset() {
return (page - 1) * recordSize;
}
현재 페이지 에서 데이터 베이스 쿼리에 사용할 OFFSET값을 계산한다. 즉 현재 페이지 번호가 3이고 페이지당 출력할 데이터 개수가 10개라면 20번째 데이터부터 3번째 페이지가 시작하는 것이다.
2.3. PagingRespose
@RequestMapping("List")
public ModelAndView list(int nowpage, BoardVo boardVo) {
// 메뉴 목록
List<MenuVo> menuList = menuMapper.getMenuList();
//------------------------------------
// 게시물 목록 조회 (페이징해서)
// 해당하는 자료수가 1 보다 작으면
// 응답 데이터에 비어있는 리스트와 null 을 담아 리턴
// count : boardVo 안의 menu_id 에 해당하는 총자료수
int count = boardPagingMapper.count( boardVo );
System.out.println( count ); // totalRecordCount
PagingResponse<BoardVo> response = null;
if( count < 1 ) { // 현재 Menu_id 조회한 자료가 없다면
response = new PagingResponse<>(
Collections.emptyList(), null);
// Collections.emptyList() : 자료는 없는 빈 리스트를 채운다
}
- boardPagingMapper.count(boardVo)를 호출하여 특정 메뉴 ID에 해당하는 게시물의 총 개수를 조회한다.
// 페이징을 위한 초기설정
SearchVo searchVo = new SearchVo();
searchVo.setPage(nowpage); // 현재 페이지 정보
searchVo.setRecordSize(10); // 페이지당 10개
searchVo.setPageSize(10); // paging.jsp 에 출력할 페이지번호수
// Pagination 설정
Pagination pagination = new Pagination(count, searchVo);
searchVo.setPagination(pagination);
- Pagination 객체를 생성하여 총 게시물 수와 SearchVo 객체를 사용하여 페이징 정보를 계산 후, searchVo에 이 pagination 객체를 설정한다.
String menu_id = boardVo.getMenu_id();
// String menu_id = "MENU02";
// 추가된 조회 옵션들
String title = boardVo.getTitle();
// String title = "Spring3";
String writer = boardVo.getWriter();
String content = boardVo.getContent();
// 추가된 조회 옵션들 끌
int offset = searchVo.getOffset();
int recordSize = searchVo.getRecordSize();
List<BoardVo> list = boardPagingMapper.getBoardPagingList(
menu_id, title, writer, content, offset, recordSize );
response = new PagingResponse<>(list, pagination);
System.out.println(response);
ModelAndView mv = new ModelAndView();
mv.addObject("menuList", menuList);
mv.addObject("menu_id", menu_id );
mv.addObject("nowpage", nowpage );
mv.addObject("searchVo", searchVo );
mv.addObject("response", response );
mv.setViewName("boardpaging/list");
return mv;
}
- 조회된 게시물 목록과 페이징 정보를 담아 PagingResponse 객체를 생성해 변수 response에 담는다.
@Getter
@ToString
public class PagingResponse<T> {
// 현재 페이지에 보여줄 db 자료 : 10 줄 짜리 record - select 결과
private List<T> list = new ArrayList<>();
// 아래의 paging.jsp 에서 사용할 변수들
private Pagination pagination;
public PagingResponse(List<T> list, Pagination pagination) {
this.list.addAll( list );
this.pagination = pagination;
}
}
조회된 게시물 목록과 페이징 정보를 PagingResponse에 담아 클라이언트에게 전달한다.
3. MyBatis 페이징
SELECT
IDX, MENU_ID, TITLE, WRITER, TO_CHAR(REGDATE, 'YYYY-MM-DD') REGDATE, HIT
FROM
BOARD
WHERE
MENU_ID = #{arg0} AND
TITLE = #{arg1} AND
WRITER = #{arg2} AND
CONTENT = #{arg3}
ORDER BY
IDX DESC
OFFSET #{arg4} ROWS FETCH NEXT #{arg5} ROWS ONLY
OFFSET: 가져올 데이터의 시작 위치이다. 여기서 #{arg4}는 limitStart로, 현재 페이지에 맞는 시작 위치를 계산하여 전달합니다.
FETCH NEXT: 지정한 수만큼의 행을 반환한다. 여기서 #{arg5}는 페이지당 데이터 수(recordSize)를 전달한다.
4. pagin.jsp
<c:if test="${ startnum gt 1 }">
<td>
<a href="/BoardPaging/List?menu_id=${menu_id}&nowpage=1">⏮</a>
</td>
<td>
<a href="/BoardPaging/List?menu_id=${menu_id}&nowpage=${startnum-1}">◀</a>
</td>
</c:if>
<c:forEach var="pagenum" begin="${startnum}" end="${endnum}" step="1">
<td>
<c:if test="${ pagenum ne 0 }">
<a href="/BoardPaging/List?menu_id=${menu_id}&nowpage=${pagenum}">
${ pagenum }
</a>
</c:if>
</td>
</c:forEach>
<c:if test="${ endnum ne totalpagecount }">
<td>
<a href="/BoardPaging/List?menu_id=${menu_id}&nowpage=${endnum+1}">▶</a>
</td>
<td>
<a href="/BoardPaging/List?menu_id=${menu_id}&nowpage=${totalpagecount}">⏭</a>
</td>
</c:if>
📍jstl (부)등호 문법
== : eq
!= : ne
< : lt
> : gt
<= : le
>= : ge
'SPRING' 카테고리의 다른 글
[Spring] 파일 업로드 및 저장 (0) | 2024.11.05 |
---|---|
[spring] Dao와 Service (2) | 2024.11.04 |
[spring]회원정보 수정 - 비밀번호 체크하기 (0) | 2024.10.13 |
[spring] 회원가입 - 아이디 중복 체크하기 (0) | 2024.10.13 |
[spring] 파라미터 다루기 (0) | 2024.10.10 |