Spring

[Spring] 게시판 페이징(Paging) 처리

jane.dev 2021. 9. 24. 23:08
반응형

테이블에서 데이터 List를 가져오는 쿼리문을 페이징 처리가 가능하도록 작성

 

테이블 구조(Oracle)

CREATE SEQUENCE board_num;

CREATE TABLE board_tbl(
    bno NUMBER(10, 0),
    title VARCHAR2(200) NOT NULL,
    content VARCHAR2(2000) NOT NULL,
    writer VARCHAR2(50) NOT NULL,
    regdate DATE DEFAULT SYSDATE,
    updatedate DATE DEFAULT SYSDATE
);

ALTER TABLE board_tbl ADD CONSTRAINT pk_board PRIMARY KEY(bno);

 

Mapper.xml과 매핑할 Mapper 인터페이스 작성

public interface Mapper{
    public List<BoardVO> getListPaging(Criteria cri);
}

 

Hint(Oracle이 데이터를 스캐닝할 때, 작성자가 직접 최적의 실행경로를 지정해주는 것)를 사용해 정렬하고,

페이징 처리된 데이터를 가져올 수 있도록 SQL문 작성(Mapper.xml)

<mapper namespace="mapper인터페이스 경로.Mapper">
  <select id="getListPaging" resultType="VO객체 경로.BoardVO">
    <![CDATA[
      SELECT bno, title, content, writer, regdate, updatedate FROM
          (SELECT /*+ INDEX_DESC(board_tbl pk_board) */
              rownum rn, board_tbl.* FROM board_tbl 
              WHERE rownum <= 페이지 번호 * 보여줄 글 개수) tbl
      WHERE rn > (페이지 번호 - 1) * 보여줄 글 개수
    ]]>
  </select>
</mapper>

/*+ INDEX_DEESC(테이블명 제약조건명) */ : 지정된 index를 내림차순으로 정렬

(MyBatis에서는 CDATA를 작성해 '<' 등의 기호가 괄호인지 비교연산자인지 알아볼 수 있도록 작성)

 

Criteria 객체를 생성해 페이지 번호와 한 페이지당 몇 개의 글을 보여줄지 지정

@Getter
@Setter
@ToString
public class Criteria{
    private int pageNum;	// 페이지 번호
    private int amount;		// 한 페이지 당 나타낼 글 개수
    
    
    public Criteria(){		// 생성자 오버로딩 - 들어온 페이지 정보가 없으면
        this(1, 10);		// 1페이지 10개의 게시물을 기본값으로 지정
    }
    
    public Criteria(int pageNum, int amount){
        this.pageNum = pageNum;	// 들어온 정보를 기반으로 수치 지정
        this.amount = amount;
    }
}

 

DTO(Data Transfer Object, 데이터 전달 객체)를 생성해 DB에 있는 정보를 토대로 가공한 데이터를 전달

2021.09.25 - [Spring] - [Spring] 페이징 처리를 위한 공식(시작 페이지와 끝 페이지)

 

[Spring] 페이징 처리를 위한 공식(시작 페이지와 끝 페이지)

버튼 개수는 1부터 10까지, 10개로 고정 끝 페이지 번호를 구하는 공식 int endPage = (int)(Math.ceil(현재 페이지 / (double)버튼 개수) * 버튼 개수); 현재 보고있는 페이지가 253일 경우, (현재 페이지 / (do..

wheneveryouwantsz.tistory.com

@Getter
@ToString
public class PageDTO{
    private int btnNum;		// 페이지네이션 버튼 개수(10개)
    private int startPage;	// 페이지 시작 번호(1~10에서는 1, 11~20에서는 11)
    private int endPage;	// 페이지 끝 번호(1~10에서는 10, 11~20에서는 20)
    
    private boolean prev, next;	// 이전, 이후 버튼 유무(1~10에서는 이전버튼이 없고, 마지막에서는 이후버튼이 없음)
    private int total;		// 전체 게시글 개수(DB에 저장된 데이터 개수)
    private Criteria cri;	// 페이지와 페이지당 글 개수
    
    // 위 선언한 변수를 가공하기위한 생성자
    public PageDTO(Criteria cri, int total, int btnNum){
        this.cri = cri;
        this.total = total;
        this.btnNum = btnNum;
        
        this.endPage = (int)(Math.ceil(cri.getPageNum() / (double)this.btnNum) * this.btnNum);
        
        this.startPage = this.endPage - this.btnNum + 1;
        
        int realEnd = (int)(Math.ceil((total * 1.0) / cri.getAmount()));
		if(realEnd < this.endPage) {
			this.endPage = realEnd;
		}
        
        // 이전 버튼은 시작 번호가 1이 아닐 경우에만 나타남
        this.prev = this.startPage == 1 ? false : true;
        // 이후 버튼은 endPage가 realEnd보다 작을 때만 나타남
        this.next = this.endPage < realEnd;
    }
}

 

Mapper를 호출할 Service 인터페이스

// service 인터페이스
public interface Service{
    public List<BoardVO> getListPaging(Criteria cri);
}

// service 인터페이스 구현
@Service
public ServiceImpl implements Service{
    @Autowired
    private Mapper mapper;
    
    @Override
    public List<BoardVO> getListPaging(Criteria cri){
        // cri의 정보인 pageNum, amount를 받아오면 해당 정보를 이용해 Mapper의 getListPaging을 호출
        return mapper.getListPaging(cri);
    }
}

 

Service를 호출해 웹브라우저의 요청을 담당하는 Controller

@Controller
public class Controller{
    @Autowired
    private Service service;
    
    // list라는 주소로 연결해 게시글 목록을 보여줌
    @GetMapping("/list")
    public void list(Criteria cri, Model model){
        // List를 만들어 service 호출
        List<BoardVO> board = service.getListPaging(cri);
        // 데이터 전달
        model.addAttribute("board", board);
        // DTO를 생성해 필요한 정보들을 넣어줌
        PageDTO dto = new PageDTO(cri, 전체 데이터개수, 페이지당 글 개수);
        model.addAttribute("dto", dto);
    }
}

 

 

Controller 수행 후, 이동하게 될 list.jsp - 버튼은 부트스트랩을 이용

<nav aria-label="...">
  <ul class="pagination">
    <!-- 이전 버튼 - dto의 prev가 true 일때만 출력 -->
    <c:if test="${dto.prev }">
      <li class="page-item">
        <!-- 이전 버튼을 누르면 현재 보고있는 페이지의 시작페이지보다 하나 더 작게 -->
        <a class="page-link" href="/list?pageNum=${dto.startPage - 1 }">Previous</a>
      </li>
    </c:if>
    
    <!-- 번호 버튼 -->
    <c:forEach var="pageNum" begin="${dto.startPage }" end="${dto.endPage }">
      <!-- 현재 페이지는 active로 표시 -->
      <li class="page-item ${dto.cri.pageNum == pageNum ? 'active' : '' }">
        <a class="page-link" href="/list?pageNum=${pageNum }">${pageNum }</a>
      </li>
    </c:forEach>
    
    <!-- 다음 페이지 버튼 -->
    <c:if test="${dto.next }">
      <li class="page-item">
        <!-- 다음 버튼을 누르면 현재 보고있는 페이지의 끝페이지보다 하나 더 크게 -->
        <a class="page-link" href="/list?pageNum=${dto.endPage + 1 }">Next</a>
      </li>
    </c:if>
  </ul>
</nav>