📖 개요
📌 오늘 할 일
◻ props 값 넘기기
◻ BoardList (게시판)
◻ Post
완성된 화면 보기 👉 Electron
데이터가 없으면 '데이터가 없습니다.' 뜨고, 데이터가 있으면 테이블로 값을 넣을 예정이다.
또한 테이블 컬럼이 5개 넘어가면 페이지네이션 +1 씩 활성화된 것을 확인 할 수 있다.
📁 폴더 구조
💻 props 값 넘기기
각 페이지마다 공통 부분이 있는 것을 알 수 있다. 파일을 3개 만들 수도 있지만, 하나의 틀을 만들고 해당 값만 변경하는 방향으로 갈 예정이다.
이 때 사용할 것이 props라는 것인데,
props(property)란 상위 컴포넌트가 하위 컴포넌트에 값을 전달할 때 사용하는 속성이다.
//App.js
<Routes>
<Route path="/server">
<Route path="" element={<BoardList title="서버 점검 안내" menu="server" />}/>
</Route>
</Routes>
<Routes>
<Route path="/all">
<Route path="" element={<BoardList title="전체 공지 사항" menu="all"/>}/>
</Route>
</Routes>
<Routes>
<Route path="/etc">
<Route path="" element={<BoardList title="개인 공지 사항" menu="etc"/>}/>
</Route>
</Routes>
title로 제목을 넘겨줄 것이고, GET으로 DB를 받기 위해 menu에 테이블 이름을 넘겨주었다.
📁 파일 설명
BoardList를 설명하기에 앞서 각 파일을 설명하겠다. 파일은 BoardList, Post, Pagination 이렇게 3개가 있는데,
BoardList는 테이블 구조를 만들고, DB 값을 받는 역할이다.
Post는 BoardList로부터 받은 데이터를 테이블에 뿌려준다.
Pagination은 BoardList에서 받은 데이터 길이로 페이지네이션을 생성해준다.
데이터 전달 방식을 도식화하면 다음과 같다.
💻 BoardList (custom axios)
DB와 통신하기 위해서 axios 모듈을 설치한다.
axios란, 브라우저, node.js를 위한 HTTP 비동기 통신 라이브러리다. (GET과 POST를 사용할 수 있는 라이브러리)
npm i axios
개발,운영계를 왔다갔다 하며 개발을 해야하는 상황이라 모든 파일에 있는 IP를 바꾸기 어려워 다음과 같이 customAxios라는 파일을 따로 생성해서 axios를 실행할 예정이다.
//customAxios.js
import axios from "axios";
export const axios_db = axios.create({
baseURL: "http://localhost:8000/",
});
소스를 간략히 설명하자면,
handlePostInfo 함수에서 axios로부터 데이터를 받아와 setData에 넣어줄 것이다.
const [Data, setData] = useState([]);
async function handlePostInfo(){
const result = await axios_db({
url : `/${props.menu}`,
method: 'GET',
headers: {
"Content-Type": "application/json"
}
})
setData(result.data.reverse())
}
위에서 자른 데이터를 postData함수를 이용해 페이지당 보여줄 데이터 건수로 나눠준다.
const limit = 5; // 페이지당 데이터 건수 (countPerPage)
const offset = (page-1)*limit; // 시작점과 끝점을 구하는 offset (첫 게시물 위치)
const postsData = (posts) => {
if(posts){
let result = posts.slice(offset, offset + limit);
return result;
}
}
이 나눠준 데이터와 App.js에서 받은 props.menu를 Post.js한테 넘겨줄 것이다.
<Post info ={postsData(Data)} menu={props.menu} />
전체 코드
import React, {useEffect, useState} from 'react';
import { axios_db } from '../components/customAxios';
import Pagination from './Pagination';
import Post from './Post';
const BoardList = (props) => {
const [Data, setData] = useState([]);
const [page, setPage] = useState(1); //페이지
const limit = 5; // 페이지당 데이터 건수 (countPerPage)
const offset = (page-1)*limit; // 시작점과 끝점을 구하는 offset (첫 게시물 위치)
const postsData = (posts) => {
if(posts){
let result = posts.slice(offset, offset + limit);
return result;
}
}
async function handlePostInfo(){
const result = await axios_db({
url : `/${props.menu}`,
method: 'GET',
headers: {
"Content-Type": "application/json"
}
})
setData(result.data.reverse())
}
useEffect(() =>{
handlePostInfo();
},[]);
return (
<div className='list'>
<div className='title'>{props.title}</div>
<div>
<table id="tblTodo" className="table-hover">
<colgroup>
<col width="*" />
<col width="30%" />
</colgroup>
<thead>
<tr>
<th>Title</th>
<th>Date</th>
</tr>
</thead>
<Post info ={postsData(Data)} menu={props.menu} />
</table>
<Pagination limit={limit} page={page} totalData={Data.length} setPage={setPage}/>
</div>
</div>
);
};
export default BoardList;
/* detail.css */
/***************************
BoardList.jsx
***************************/
.list { /* list css */
background-color: white;
z-index: 9;
top: 10px;
left: 215px;
height: 550px;
width: 650px;
padding:20px;
border-right: 1px solid rgba(0, 0, 0, 0.07);
position: fixed;
box-shadow: 0 0px 24px 0 rgb(0 0 0 / 6%), 0 1px 0px 0 rgb(0 0 0 / 2%);
}
.detail { /* 내용 css */
background-color: white;
z-index: 9;
top: 10px;
left: 215px;
height: 550px;
width: 650px;
padding-top: 10px;
border-right: 1px solid rgba(0, 0, 0, 0.07);
position: fixed;
box-shadow: 0 0px 24px 0 rgb(0 0 0 / 6%), 0 1px 0px 0 rgb(0 0 0 / 2%);
}
.title{ /* list Title */
font-size:20px;
font-weight: bold;
text-align: left;
padding: 10px;
color: #01388a;
}
💻 Post
Post는 BardList에서 보내준 info를 가지고 페이지를 만들어준다. (menu는 추후에 이용할 예정이며 잠시 보류하자!)
info.map을 통해 데이터를 뿌려주는데, 이 때 조건식 하나를 추가했다.
info.length 가 0이 아니라면 데이터를 뿌려주고, 0이라면 '데이터가 없습니다'를 보여줄 것이다.
React의 조건문은 다양하게 있는데, 여기서 쓴 조건문은 아래와 같다.
{조건식 ? 참이면 보여줄 예제 : 거짓이면 보여줄 예제}
전체 코드
//Post.js
function Post ({info}){
return (
<tbody>
{
info.length !== 0 ? info.map((data)=> {
return (
<tr key={data.idx}>
<td>{data.Title}</td>
<td>{data.Date}</td>
</tr>
)})
: (
<tr>
<td colSpan={2}>데이터가 없습니다.</td>
<td></td>
</tr>
)
}
</tbody>
)
}
export default Post;
💻 Pagination
페이지네이션은 추후 자세히 알아보도록 하고, 소스부터 포스팅하겠다.
//Pagination.js
import {useState, useEffect} from 'react';
import {useLocation} from "react-router-dom";
import "../css/Paging.css";
function Pagination ({page, totalData, limit, setPage}){
const location = useLocation();
const [currPage, setCurrPage] = useState(page);
const totalPage = Math.ceil(totalData / limit) ;
const showPageCnt = 5; // 화면에 보일 페이지 번호 개수
let showPage = Math.ceil(currPage/showPageCnt); // 화면에 보여질 페이지 그룹
let firstPage = ((showPage -1) * showPageCnt) + 1; //화면에 보여질 페이지의 첫번째 페이지 번호
let lastPage = showPage * showPageCnt; // 화면에 보여질 페이지의 마지막 페이지 번호
let lastshowPage = Math.ceil(totalPage/showPageCnt); // 마지막 페이지 그룹
const prevPage = currPage - (currPage % showPageCnt) - (showPageCnt - 1);
const nextPage = currPage - (currPage % showPageCnt) + (showPageCnt + 1);
useEffect(() =>{
const pageNo = location.state?.pageNo || '1' ;
setCurrPage(pageNo);
setPage(pageNo);
},[]);
return (
<section className='paginator'>
<nav className='Nav'>
<button //첫 페이지
className='Button'
onClick={() => {setPage(1); setCurrPage(1);}}
disabled={page<=5}>
<<
</button>
<button //이전 페이지
className='Button'
onClick={() => {setPage(prevPage); setCurrPage(prevPage);}}
disabled={page<=5}>
<
</button>
{Array(5).fill().map((_, i) =>{
return (
<button
className='Button'
key={i}
onClick={() => {setPage(firstPage+i)}}
aria-current={(page == firstPage+i) && "page"}
disabled={(firstPage+i)>totalPage}
>
{firstPage+i}
</button>
)
}
)}
<button //다음 페이지
className='Button'
onClick={() => {setPage(nextPage); setCurrPage(nextPage);}}
disabled={showPage == lastshowPage || totalData == 0}>
>
</button>
<button //마지막 페이지
className='Button'
onClick={() => {setPage(totalPage); setCurrPage(totalPage);}}
disabled={showPage == lastshowPage || totalData == 0}>
>>
</button>
</nav>
</section>
)
}
export default Pagination;
/*Paging.css*/
/***************************
Pagination.jsx
***************************/
.paginator{ /*<section> 처음*/
position:absolute;
bottom:2%;
left: 50%;
transform: translate(-50%);
}
.Nav{ /*<nav> Style*/
display: flex;
gap: 4px;
}
/***************************
버튼 CSS
***************************/
.Button{
height: 25px;
display: flex;
align-items: center;
justify-content: center;
border: none;
border-radius: 8px;
padding: 8px;
margin: 0;
background: gray;
color: white;
font-size: 13px;
}
.Button:hover{
background: #005bac;
cursor: pointer;
transform: translateY(-2px);
}
.Button:disabled{
background: gainsboro;
cursor: revert;
transform: revert;
}
.Button[aria-current="page"]{
background: #005bac;
font-weight: bold;
cursor: revert;
transform: revert;
}
.Button.active{
background: #005bac;
font-weight: bold;
cursor: revert;
transform: revert;
}
📌 참고 사이트
'💻 프로젝트 > 02. 사내 공지 데스크톱 앱' 카테고리의 다른 글
『Server 06 』[React] 상단 메뉴바 (0) | 2023.09.20 |
---|---|
『Server 05 』[React] [node.js] 로그인 화면 (0) | 2023.09.19 |
『Server 04 』[express] [mysql2] Node.js 서버 개설, DB 연동(GET) (0) | 2023.09.17 |
『Client 03 』 [React] [Electron] 정보 탭 CSS(Grid), 내 pc IP, Host 가져오기 (0) | 2023.09.17 |
『Client 02 』 [React] [Electron] React 사이드 메뉴, Electron Icon 변경하기 (0) | 2023.09.16 |