스프링수업 18일차

한 일

  • 페이징
  • 페이지네이션

페이징

추후 개념보충 필요

오라클의 ROWNUM을 이용한 방법
1. 모든 데이터의 행에 순서를 매긴 후 필요한 순번을 가져오기
2. 필요한 데이터의 순번까지만 순서를 매긴 후 바로 가져오기

MySQL의 LIMIT을 이용한 방법
LIMIT <skip> <count>

# 처음부터 3개 건너뛴 후 4개 출력
SELECT * FROM board LIMIT 3, 4;

페이징 객체 생성


페이지를 처리할 변수를 저장하기 위한 클래스 생성

- Criteria -

public class Criteria {

	private int pageNum;	// 현재 페이지
	private int amount;	// 한 페이지당 보여질 게시물 수
	private int skip;	// 스킵할 게시물 수 ((pageNum - 1 ) * amount)

	// 기본생성자 => 기본세팅: pageNum = 1, amount = 10
	public Criteria() {
		this(1, 10);	// 전체 생성자를 통해 (1, 10)을 입력해 객체 생성
	}

	// 생성자 => 원하는 pageNum, 원하는 amount
	public Criteria(int pageNum, int amount) {
		this.pageNum = pageNum;
		this.amount = amount;
		this.skip = (pageNum - 1 ) * amount;
	}

	public int getPageNum() {
		return pageNum;
	}

	// 새로 페이지 수를 설정했을 때 skip도 계산
	public void setPageNum(int pageNum) {
		this.pageNum = pageNum;
		this.skip = (pageNum - 1 ) * amount;
	}

	public int getAmount() {
		return amount;
	}

	// 페이지당 데이터 갯수를 바꿀 경우에도 skip을 다시 계산
	public void setAmount(int amount) {
		this.amount = amount;
		this.skip = (pageNum - 1 ) * amount;
	}

	public int getSkip() {
		return skip;
	}

	public void setSkip(int skip) {
		this.skip = skip;
	}

	@Override
	public String toString() {
		return "Criteria [pageNum=" + pageNum + ", amount=" + amount + ", skip=" + skip + "]";
	}
}

skip(뛰어넘을 게시글 수)은 계산을 통해 만들어짐. 입력받는 필드는 pageNum과 amount만.
skip은 계산으로 산출되는 필드이므로 pageNum이나 amount의 숫자가 변경될 경우 다시 계산하도록 set메서드 생성 시 계산식을 작성해준다.


페이징 쿼리문 작성

- BoardMapper -

// 게시판 목록(페이징 적용). pageNum과 amount를 입력받아 객체 cri생성. 없으면 기본값(1, 10)적용
public List<BoardVO> getListPaging(Criteria cri);		

페이징을 적용한 게시판목록을 불러오는 List를 추가.

- BoardMapper.xml -

<!-- 게시판 목록 (페이징) : skip과 amount는 Criteria 객체에서 입력됨 -->
<select id="getListPaging" resultType="BoardVO">
	SELECT * FROM (
			SELECT bno, title, writer, regdate, updatedate
			FROM board ORDER BY bno DESC) as T1
	LIMIT #{skip}, #{amount}
</select>

bno가 큰순 = 최신 게시글이 위로 가장 올라오는 순서로 불러옴

TRUNCATE TABLE board;	# 모든 행 삭제

# 프로시저 시작
DELIMITER $$	
DROP PROCEDURE IF EXISTS procedureName$$
 
CREATE PROCEDURE procedureName()
BEGIN
    DECLARE i INT DEFAULT 1;
        
    WHILE i <= 100 DO
        INSERT INTO board(title , content, writer, regDate, updateDate)
          VALUES( concat('제목',i), '테스트 데이터 에요', '작가' , now(),  now());
        SET i = i + 1;
    END WHILE;
END$$
DELIMITER $$

CALL procedureName; 	# 프로시저 호출

프로시저 내부에 한글주석이 있으면 실행이 안되니 주의.
프로시저를 실행하면 데이터 100개가 insert됨.

테스트

- BoardMapperTests -

// 페이징 테스트		
 @Test
 public void testGetListPaging() {
     Criteria cri = new Criteria();
     List<BoardVO> list = boardMapper.getListPaging(cri);
     list.forEach(board -> log.info("" + board));
 }


기본값대로 1페이지, 한 페이지당 10개의 게시글을 보여줌

pageNum과 amount를 2, 5로 바꿔서 테스트해보기.

@Test
 public void testGetListPaging() {
     Criteria cri = new Criteria();
     cri.setPageNum(2);
     cri.setAmount(5);
     List<BoardVO> list = boardMapper.getListPaging(cri);
     list.forEach(board -> log.info("" + board));
 }

service에 페이징 적용

- BoardService -

public List<BoardVO> getListPaging(Criteria cri);	// 페이징 적용 게시글목록

- BoardServiceImpl -

@Override
public List<BoardVO> getListPaging(Criteria cri) {
	return boardMapper.getListPaging(cri);
}

- BoardController -

@GetMapping("/list")
public String boardListGet(Criteria cri, Model model) {
	// boardList에 모든 게시글을 전달
	model.addAttribute("boardList", boardService.getListPaging(cri));
	return "list";
}

원래있던 boardListGet메서드는 주석처리 후 페이징 된 게시글리스트를 전달하도록 새 메서드 생성.
cri에 값을 지정해주지 않으면 기본값인 (1, 10)이 적용되고, 값을 따로 정해주면 그 값대로 현재페이지와 한 페이지당 게시글 갯수가 변경됨.

http://localhost:8080/board/list

/list로 접근 시 기본페이징인 가장 첫 페이지만 출력됨

http://localhost:8080/board/list?pageNum=5&amount=15

parameter로 pageNum, amount에 값을 넘겨 현재페이지와 한페이지당 게시글수를 변경할 수 있음


페이지네이션


페이지 계산을 위한 클래스생성

- PageMakerDTO -

private int startPage;		// 시작 페이지
private int endPage;		// 끝 페이지
private boolean prev, next;	// 이전, 다음페이지 존재유무
private int total;			// 전체 게시글 수
private Criteria cri; 		// 현재 페이지, 페이지당 게시글 표시 수 정보

public PageMakerDTO(int total, Criteria cri) {
	this.total = total;
	this.cri = cri;

	// 마지막페이지(현재 페이지네이션바의 마지막 숫자): 10 단위로 표시 1~10, 11~20, 21~30
	// Math.ceil은 소수점을 정수로 올림
	this.endPage = (int)(Math.ceil(cri.getPageNum() / 10.0)) * 10;
	
	// 시작 페이지
	this.startPage = this.endPage - 9;
	
	// 실제 마지막 페이지 (총 마지막 페이지)
	int realEnd = (int)(Math.ceil(total * 1.0 / cri.getAmount()));
	
	// 전체 마지막 페이지(realEnd)가 화면에 보이는 마지막페이지(endPage)보다 작은 경우, 보이는 페이지(endPage) 값 조절
	if(realEnd < this.endPage) {
		this.endPage = realEnd;
	}

	// < 이전페이지 참? 시작 페이지(startPage)값이 1보다 큰 경우 true
    this.prev = this.startPage > 1;
    
    // > 다음페이지 참? 마지막 페이지(endPage)값이 1보다 큰 경우 true
    this.next = this.endPage < realEnd;
}
// get/set toString 메서드 자동생성

ceil은 올림함수
pageNum이 1이면 0.1 => 올림으로 0.1~1.0까지는 모두 1이 됨. 즉 pageNum이 1~10까지는 1이 됨.
=> 현재 페이지가 1 ~ 9라면 끝 페이지는 모두 10이 된다는 뜻. 페이지네이션바에 1부터 10까지의 페이지숫자가 나오게 됨.

- BoardMapper -

public int getTotal();	// 게시글 총 갯수

총 게시글 수를 계산하는 메서드 추가

- BoardMapper -

<!-- 게시글 총 갯수 -->
<select id="getTotal" resultType="int">
	SELECT count(*) FROM board;
</select>

테스트

- BoardMapperTests -

// 총 게시글 수
 @Test
 public void testGetTotal() {
	 int result = boardMapper.getTotal();
	 log.info("총 개시글 수: " + result);
 }

service에 페이지네이션 적용

- BoardService -

public int getTotal();	// 게시글 총 갯수

- BoardServiceImpl -

@Override
public int getTotal() {
	return boardMapper.getTotal();
}

- BoardController -
boardListGet 수정

// 게시글 목록 페이지 (페이징 적용)
@GetMapping("/list")
public String boardListGet(Criteria cri, Model model) {
	// boardList에 페이징 된 모든 게시글을 전달
	model.addAttribute("boardList", boardService.getListPaging(cri));
	
	int total = boardService.getTotal();
	PageMakerDTO pmk = new PageMakerDTO(total, cri);	// 객체 생성 시 모든 변수 계산됨
	
	model.addAttribute("pmk", pmk);		// 페이지네이션을 위한 pmk객체 전달
	
	return "list";
}

- list.html -

<nav aria-label="Page navigation" th:if="${pmk.endPage > 0}">
  <ul class="pagination">
    <li class="page-item">
      <a class="page-link" href="javascript:;" aria-label="이전">
        <span class="material-icons"> keyboard_arrow_left </span>
        <span class="sr-only">이전</span>
      </a>
    </li>

    <li class="page-item" th:each="number : ${#numbers.sequence(1, pmk.endPage)}"><a class="page-link" href="javascript:;" th:text="${number}"></a></li>

    <li class="page-item">
      <a class="page-link" href="javascript:;" aria-label="다음">
        <span class="material-icons"> keyboard_arrow_right </span>
        <span class="sr-only">다음</span>
      </a>
    </li>
  </ul>
</nav>

좋은 웹페이지 즐겨찾기