2021. 10. 14. 15:16ㆍJAVA/Spring
이전 글
https://yonghwankim-dev.tistory.com/144
SpringBoot #6 SpringBoot+React 기반 간단한 게시판 생성하기 #4 게시판 관련 컴포넌트 및 Router 컴포넌트
이전글 https://yonghwankim-dev.tistory.com/143 SpringBoot #6 SpringBoot+React 기반 간단한 게시판 생성하기 #3 React 프로젝트 생성 이전글 https://yonghwankim-dev.tistory.com/146 SpringBoot #6 SpringBo..
yonghwankim-dev.tistory.com
개요
이전 글에서는 React+SpringBoot+MySQL 연동을 완료하여 데이터베이스에 존재하는 게시판의 게시물 샘플 데이터를 조회하여 웹 페이지에 출력하는 것을 구현하였습니다. 이번 글에서는 게시물의 페이지 번호를 웹 페에지에 출력하도록 하고 기능을 구현하는 것을 목표로 합니다.
1. React : 페이지 번호 구현
src/component/webBoard/WebBoardListComponent.jsx 수정
import React, {Component} from 'react';
import ApiService from "../../ApiService";
import queryStirng from 'query-string';
class WebBoardListComponent extends Component{
constructor(props){
super(props);
this.state = {
result : null,
prevPage : null,
nextPage : null,
pageList : [],
boards : [],
page : 1,
size : 10,
type : "",
keyword : "",
}
}
componentDidMount(){
this.reloadWebBoardList(this.state.page, this.state.size, this.state.type, this.state.keyword);
}
reloadWebBoardList = (page=1, size=10, type="", keyword="")=>{
ApiService.fetchWebBoards(page, size, type, keyword)
.then(res=>{
this.setState({
result : res.data,
prevPage : res.data.prevPage,
nextPage : res.data.nextPage,
pageList : res.data.pageList,
boards:res.data.result.content
})
})
.catch(err=>{console.log("reloadWebBoardList() Error!",err);});
}
onChangePage = (p)=>{
this.setState({page : p});
this.reloadWebBoardList(p,this.state.size, this.state.type, this.state.keyword);
}
onChangeType = (e)=>{
this.setState({type : e.target.value});
}
onChangeKeyword = (e)=>{
this.setState({keyword : e.target.value});
}
onClickSearch = ()=>{
this.reloadWebBoardList(this.state.page, this.state.size, this.state.type, this.state.keyword);
}
render(){
return(
<>
<div>
<h2>WebBoard List</h2>
<table>
<thead>
<tr>
<th>Bno</th>
<th>Title</th>
<th>Wirter</th>
<th>Content</th>
<th>Regdate</th>
<th>Updatedate</th>
</tr>
</thead>
<tbody>
{
this.state.boards.map( board=>
<tr key={board.bno}>
<td>{board.bno}</td>
<td>{board.title}</td>
<td>{board.writer}</td>
<td>{board.content}</td>
<td>{board.regdate}</td>
<td>{board.updatedate}</td>
</tr>
)
}
</tbody>
</table>
{/* search */}
<div>
<select name="searchType" onChange={this.onChangeType}>
<option value="">--</option>
<option value='t'>Title</option>
<option value='w'>Writer</option>
<option value='c'>Content</option>
</select>
<input type="text" onChange={this.onChangeKeyword}/>
<button onClick={this.onClickSearch}>Search</button>
</div>
</div>
<div>
{/* pagination */}
<ul>
{
/* PREV 버튼 */
this.state.prevPage===null ? null : <li><button onClick={()=>{this.onChangePage(this.state.prevPage.pageNumber+1)}}>PREV {this.state.prevPage.pageNumber+1}</button></li>
}
{
/* 페이지 번호 버튼 */
this.state.pageList.map(page =>
<li key={page.pageNumber+1}>
{
this.state.result.currentPageNum-1===page.pageNumber ?
<button onClick={()=>{this.onChangePage(page.pageNumber+1)}} style={{color:"red"}}>{page.pageNumber+1}</button>
:
<button onClick={()=>{this.onChangePage(page.pageNumber+1)}}>{page.pageNumber+1}</button>
}
</li>
)
}
{
/* NEXT 버튼 */
this.state.nextPage===null ? null : <li><button onClick={()=>{this.onChangePage(this.state.nextPage.pageNumber+1)}}>NEXT {this.state.nextPage.pageNumber+1}</button></li>
}
</ul>
</div>
</>
);
}
}
export default WebBoardListComponent;
2. React : ApiService.js 수정
src/ApiService.js 수정
import axios from 'axios';
const BOARD_API_BASE_URL = "http://localhost:8080/boards";
class ApiService{
fetchWebBoards(page, size, type, keyword){
return axios.get(BOARD_API_BASE_URL + "/list?page="+page+"&size="+size+"&type="+type+"&keyword="+keyword);
}
}
export default new ApiService();
3. SpringBoot : 페이지 번호 출력
페이지 처리에는 Page<WebBoard>의 getPageable()을 이용해서 Pageable 타입의 객체를 활용합니다. Pageable 객체는 다음과 같은 구조로 이루어져 있습니다.
주어지는 Pageable 객체를 이용해서 화면에 간단하게 이전 페이지와 다음 페이지만을 보여주는 것이라면 문제가 없지만, 일반적인 웹 페이지처럼 페이지의 번호를 보여주려면 시작 페이지까지 몇 번 prevOrFirst()를 해야하고, next()를 해야 하는지 계산해야합니다.
페이지 번호를 출력하려면 프로젝트에서 org.zerock.vo 패키지에 PageMaker라는 별도의 클래스를 이용해, 페이지 번호 출력에 필요한 정보들을 처리하도록 저장합니다.
org.zerock.vo.PageMaker 클래스 생성
package org.zerock.vo;
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.java.Log;
@Getter
@ToString(exclude = "pageList")
@Log
public class PageMaker<T> {
private Page<T> result;
private Pageable prevPage; // 페이지 목록의 맨 앞인 '이전'으로 이동하는데 필요한 정보를 가진 Pageable
private Pageable nextPage; // 페이지 목록의 맨 뒤인 '다음'으로 이동하는데 필요한 정보를 가진 Pageable
private int currentPageNum; // 현재 페이지 수
private int totalPageNum; // 전체 페이지 수
private Pageable currentPage; // 현재 페이지의 정보를 가진 Pageable
private List<Pageable> pageList; // 페이지 번호의 시작부터 끝까지의 Pabeable들을 저장한 리스트
public PageMaker(Page<T> result) {
this.result = result;
this.currentPage = result.getPageable();
this.currentPageNum = result.getNumber() + 1; // result.getNumber는 0부터 시작
this.totalPageNum = result.getTotalPages();
this.pageList = new ArrayList<Pageable>();
calcPages();
}
private void calcPages() {
// currentPageNum==1 => tempEndNum = 10, startNum = 1
// currentPageNum==11 => tempEndNum = 20, startNum = 11
// currentPageNum==21 => tempEndNum = 30, startNum = 21
int tempEndNum = (int) (Math.ceil(this.currentPageNum/10.0)*10);
int startNum = tempEndNum - 9;
Pageable startPage = this.currentPage;
// 현재 페이지 번호를 기준으로 (현재 페이지 번호-10)번째 Pageable 정보를 가져온다. (이전 버튼)
for(int i=startNum; i<this.currentPageNum;i++)
{
startPage = startPage.previousOrFirst();
}
this.prevPage = startPage.getPageNumber() <= 0 ? null : startPage.previousOrFirst();
log.info("tempEndNum: " + tempEndNum);
log.info("total: " + totalPageNum);
// 전체페이지 개수가 10페이지단위 종료보다 작은 경우 끝 페이지 수를 변경
if(this.totalPageNum < tempEndNum)
{
tempEndNum = this.totalPageNum;
this.nextPage = null; // 다음 버튼 비활성화
}
// 페이지 정보를 가지는 Pageable 객체를 리스트에 저장
for(int i=startNum; i<=tempEndNum;i++)
{
pageList.add(startPage);
startPage = startPage.next();
}
// 다음 페이지가 남아있다면 다음 페이지 정보를 가지는 pageable 객체 저장
this.nextPage = startPage.getPageNumber() +1 < totalPageNum ? startPage : null;
}
}
org.zerock.controller.WebBoardController.java 수정
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/boards/")
@Log
public class WebBoardController {
@Autowired
private WebBoardRepository repo;
@GetMapping("/list")
public PageMaker<WebBoard> list(PageVO vo) {
Pageable page = vo.makePageable(0, "bno");
Page<WebBoard> result = repo.findAll(repo.makePredicate(null, null), page);
log.info(""+page);
log.info(""+result);
return new PageMaker<WebBoard>(result);
}
}
list() 메서드의 수정된 부분은 기존 Page<WebBoard> 객체를 반환하는 것이 아닌 PageMaker<WebBoard> 객체 타입을 반환하는 것으로 수정되었습니다. 그래서 객체를 생성하면 생성자안에서 필요한 정보를 처리할 것입니다.
4. 프로젝트 실행 및 확인
React 및 SpringBoot 프로젝트를 실행하고, 브라우저에서 'http://localhost:3000/boards/list'를 입력하여 페이지 번호가 출력되고 제대로 기능하는지 확인합니다.
아래 실행 결과는 NEXT 11버튼을 클릭하였을 때 결과입니다.
References
source code : https://github.com/yonghwankim-dev/SpringBoot-Study
스타트 스프링 부트, 구멍가게 코딩단 지음