[TIL] Day39 - 페이징

JIONY·2022년 10월 16일
0

TIL - Web BE - Spring Boot

목록 보기
15/20

페이징 구현하는 거 별 거 아닌 줄 알았다가 ㅋㅋㅋ 과정이 안끝나서 ??? 했음 ㅋㅋㅋㅋ


페이징

계획

  • rownum으로 10개씩 자르기
    - 게시글 번호는 결번이 있을 수 있으므로 rownum 사용

변수 설정

  • 페이지당 row: size
    • size=10
    • 우선 size를 고정하는 방식으로 구현
  • 페이지: p
  • 시작 행번호: startRow
    • endRow-(size-1)
  • 끝 행번호: endRow
    • p * size

VO 업데이트

  • VO에 현재 페이지 변수를 추가(알아서 받아오도록 하기 위함)
    • default value를 1로 설정(생성자 만들지 않고 변수에 바로 대입하면 p가 없을 때 기본값으로 1이 들어감)
  • @ToString.Include
    • 메소드 위에 롬복 추가하면 메소드도 toString에 포함돼서 출력됨

      private int p = 1;
      private int size = 10;
      @ToString.Include
      public int endRow(){
      	return p * size;
      }
      @ToString.Include
      public int startRow(){
      	return endRow() - (size - 1);
      }

매핑 주소 업데이트

  • 게시글 목록 조회하는 매핑의 매개변수에 페이지(p)를 파라미터로 추가
    • @RequestParam(required = false, defaultValue = "1") int p

조회 메소드 업데이트

VO에 현재 페이지 변수를 추가해 받아오도록 했는데, 목록 조회 메소드는 매개변수로 VO를 받고 있지 않아서 페이징이 적용되지 않음. 목록과 검색 결과 조회 메소드가 분리되어 있는데, 통합 조회 메소드를 새로 추가해서 해당 메소드에서 검색인지 목록인지를 구분해 각 메소드를 호출하도록 수정


통합 검색 메소드(목록+검색)

//Dao
List<BoardDto> selectList(BoardListSearchVO vo);
List<BoardDto> list(BoardListSearchVO vo);
List<BoardDto> search(BoardListSearchVO vo);
//DaoImpl
public List<BoardDto> selectList(BoardListSearchVO vo) {
    if(vo.isSearch()) {//검색이라면
    	return 검색메소드;
    }else {
    	return 목록메소드;
    }
}

컨트롤러

  • DAO에서 만든 트랜잭션을 컨트롤러에서 불러와서 처리
  • model에 VO를 담은 조회 구문을 첨부하면 Dao가 알아서 검색/조회 구분
    • model.addAttribute("list", boardDao.selectList(vo));


페이지 내비게이터

마지막 페이지

변수 설정

마지막 페이지 번호를 구하려면 총 데이터 개수를 알아야 함

  • 필요한 데이터: 현재 총 결과 데이터 개수(목록, 검색일 때 다름), size
    • 현재 총 결과 데이터 개수: count

VO 업데이트

//VO
//총 게시글 수
@ToString.Include
private int count;

DB 명령 추가

  • 검색과 목록의 총 결과 데이터 개수를 구하는 메소드 추가
  • 위에서 한 것과 마찬가지로 통합 메소드에서 목록/검색 구분하도록 처리
//Dao
//검색과 목록의 총 결과 데이터 개수를 구하는 메소드
int count(BoardListSearchVO vo);
int searchCount(BoardListSearchVO vo);
int listCount(BoardListSearchVO vo);
//DaoImpl
@Override
public int count(BoardListSearchVO vo) {
	if(vo.isSearch()) {
		return searchCount(vo);
	}else {
		return listCount(vo);
	}
}
	
@Override
public int searchCount(BoardListSearchVO vo) {
	String sql = "select count(*) "
			+ "from board "
			+ "where instr(#1, ?) > 0";
	sql = sql.replace("#1", vo.getType());
	Object[] param = {vo.getKeyword()};
	return jdbcTemplate.queryForObject(sql, int.class, param);
}
	
@Override
public int listCount(BoardListSearchVO vo) {
	String sql = "select count(*) from board";
	return jdbcTemplate.queryForObject(sql, int.class);
}

컨트롤러

//list
//페이지 네비게이터를 위한 게시글 수 구하기
int count = boardDao.count(vo);
//count도 vo에 첨부 -> model로 자동으로 넘길 수 있도록
vo.setCount(count);

block

페이지 내비게이터에 있는 숫자 하나하나를 블록이라고 함

변수 설정

  • 페이지 내비게이터의 블록 개수: blockSize
    • blockSize = 10
  • 마지막 페이지: pageCount
    • 총 pageCount 개의 페이지가 있음
  • 시작/끝 블록: startBlock, endBlock
    • 페이지 내비게이터는 startBlock~endBlock까지 표시됨
    • 현재 페이지가 12라면, 페이지 내비게이터는 11~20이 표시되어야 함

VO 업데이트

//화면에 표시할 page block 수(default=10)
private int blockSize = 10;

마지막 페이지

  • 총 페이지 수는 총 결과 개수(count)를 한 페이지에 보여줄 데이터 수(size)로 나누면 됨
  • 1~9는 규칙에 어긋남 → count+9를 하면 됨
    • 더하는 숫자 9는 size-1과 같음
  • pageCount = count + (size - 1) / size


시작/끝 블록

현재 페이지(p)에 따른 시작/끝 블록(start/end Block) 설정이 필요함

  • p를 기준으로 끝 블록을 계산하고, 끝 블록으로 시작 블록 구하기
  • endBlock = (p + blockSize - 1) / blockSize * blockSize
  • startBlock = endBlock - 9


//마지막 페이지
@ToString.Include
public int pageCount() {
	return (count + size - 1) / size;
}

//시작 블록
public int StartBlock() {
	return endBlock() - 9;
}

//끝 블록
public int endBlock() {
	return (p + blockSize - 1) / blockSize * blockSize;
}

list.jsp

화면에 페이지 내비게이터 추가

  • 반복문에 범위 지정, 링크
<!-- 페이지 내비게이터 -->
<h3>
	&laquo; 
	&lt; 
	<c:forEach var="i" begin="${vo.startBlock}" end="${vo.endBlock}" step="1">
		<a href="list?p=${i}">${i}</a>
	</c:forEach>
	&gt; 
	&raquo;
</h3>

이전, 다음

<a href="list?p=${vo.startBlock()-1}"></a>이렇게 해도 되지만, 의미가 있으면 이름을 붙여 메소드로 만들고 호출해오는 객체지향 코드를 작성(유지보수 용이)

  • 이전페이지: prevBlock()
    • <a href="list?p=${vo.prevBlock()}"><</a>
  • 다음페이지: nextBlock()
  • 첫 페이지: firstBlock()
  • 끝 페이지: lastBlock()
//VO
@ToString.Include
public int prevBlock() {	
	return startBlock() - 1;
}
	
@ToString.Include
public int nextBlock() {
	return endBlock() + 1;
}
	
@ToString.Include
public int firstBlock() {
	return 1;
}
	
@ToString.Include
public int lastBlock() {
	return pageCount();
}


문제점 해결

첫/이전 페이지 버튼 조건

  1. 현재 첫 페이지가 아닐 때에만 첫 페이지로 이동하도록 조건 추가
  2. 현재 첫 구간이 아닐 때에만 이전 버튼 작동하도록 조건 추가
  • 이전 버튼 클릭 시, 이전 구간의 마지막 페이지로 이동
<!-- 현재 첫 페이지가 아니라면: 메소드화
	<c:when test="${vo.p > 1}">
-->

<!-- 현재 첫 페이지가 아니라면 -->
<c:when test="${not vo.isFirst()}">
	<!-- 첫 페이지로 이동 -->
	<a href="list?p=${vo.firstBlock()}">&laquo;</a> 
</c:when>

<c:choose>
	<!-- 현재 첫 구간이 아니라면 = startBlock이 1이 아니면 -->
	<c:when test="${vo.startBlock() > 1}">
		<!-- 이전을 누르면 이전 구간의 마지막 페이지로 안내 -->
		<a href="list?p=${vo.prevBlock()}">&lt;</a> 
	</c:when>
	<c:otherwise>
		<a href="#">&laquo;</a>
	</c:otherwise>
</c:choose>

메소드화

//첫 페이지 검사
@ToString.Include
public boolean isFirst() {
	return p == 1;
}

//현재 구간 검사(이전 구간이 있는지)	
@ToString.Include
public boolean hasPrev() {
	return startBlock() > 1;
}

끝/다음 페이지 버튼 조건

  1. 현재 끝 페이지가 아닐 때에만 끝 페이지로 이동하도록 조건 추가
  2. 현재 끝 구간이 아닐 때에만 다음 버튼 작동하도록 조건 추가
    • 다음 버튼 클릭 시, 다음 구간의 첫 페이지로 이동
<!-- 다음을 누르면 다음 구간의 첫 페이지로 안내 -->
<c:choose>
	<c:when test="${vo.hasNext()}">
		<a href="list?p=${vo.nextBlock()}&${vo.parameter()}">&gt;</a>
	</c:when>
	<c:otherwise>
		<a href="#">&gt;</a>
	</c:otherwise>
	</c:choose>
	
	<c:choose>
	<c:when test="${not vo.isLast()}">
		<a href="list?p=${vo.lastBlock()}&${vo.parameter()}">&raquo;</a>
	</c:when>
	<c:otherwise>
		<a href="#">&raquo;</a>
	</c:otherwise>
</c:choose>

endBlock 수정

데이터가 존재하는 마지막 페이지를 끝페이지로 설정

  • lastBlock과 endBlock 중 작은 값을 끝 번호로 설정: Math.min(a, b) 활용
    • last: 전체 중에 마지막
    • end: 현재 구간의 마지막


startBlock 수정

endBlock을 기준으로 startBlock을 구하는 방식을 사용했던 것이 문제가 됨

@ToString.Include
public int startBlock() {
	return (p - 1) / blockSize * blockSize + 1;
}
	
@ToString.Include
public int endBlock() {
	int value = startBlock() + blockSize - 1;
	return Math.min(value, lastBlock());
}

메소드화

//마지막 페이지 검사
@ToString.Include
public boolean isLast() {
	return endBlock() == lastBlock();
}

//현재 구간 검사(다음 구간이 있는지)	
@ToString.Include
public boolean hasNext() {
	return endBlock() < lastBlock();
}

검색 결과/size

검색 후 다음 페이지 넘어가면 검색 조건이 풀림

  • 원인: 페이지 링크에 검색어를 안넣었기 때문 → 모든 링크에 검색일 때는 타입과 키워드를 파라미터로 넘기도록 설정 추가

size 바꾸고 다음페이지 넘어가면 변경 size 설정 풀림

  • size가 유지될 수 있도록 파라미터 추가

VO 업데이트

파라미터로 불러올 메소드 추가

  • 검색/목록을 구분해 검색 조건과 크기 등이 유지될 수 있도록 Query String 생성
    • p(현재 페이지)를 제외한 나머지 항목들에 대해 파라미터 생성
    • p는 그때 그때 가져오고 있음
//VO
public String parameter() {
	if(isSearch()) {
		return "size="+size+"&type="+type+"&keyword="+keyword;
	}else {
		return "size="+size;
	}
}

링크 업데이트

모든 링크에 &${vo.parameter()} 추가

//list.jsp
//ex.
<a href="list?p=${vo.nextBlock()}&${vo.parameter()}">&gt;</a>


0개의 댓글