배운것들을 활용해서 게시판을 만들어 보았다.
크게 4개의 컴포넌트로 구상하였는데
첫번째는 게시글 목록을 보여줄 List 컴포넌트, 두번째는 목록에서 제목을 누르면 내용을 보여줄 상세페이지
Detail컴포넌트, 세번째는 상세페이지에서 내용을 바꿀 수 있게 해주는 수정페이지 Modify 컴포넌트,
네번째는 게시글 목록에서 글쓰기버튼으로 게시글을 추가하게해주는 Create 컴포넌트이다.
일단 다 기본 틀만 만들어놓고 서버에 필요한것들을 다 인스톨 한 뒤
server.js 에 이렇게 작성해준다.
const express = require('express');
const bodyParser = require('body-parser');
const mysql = require('mysql');
const cors = require('cors');
//express 사용하기 위한 app 생성
const app = express();
//express 사용할 서버포트 설정
const PORT = 5000;
app.use(cors());
app.use(bodyParser.json());
//DB 접속
const db = mysql.createConnection({
host : 'localhost',
user: 'react',
password: 'mysql',
port:'3306',
database:'db_react'
});
// express 접속
app.listen(PORT, ()=>{
console.log(`server connecting on : http://localhost:${PORT}`);
});
//db 연결
db.connect((err)=>{
if(!err){
console.log("seccuss");
}else{
console.log("fail");
}
});
// --------- DB에서 값을 가져오기 -------------
// / => root 연결시 보여지는 기본화면 설정
app.get('/', (req, res) =>{
res.send('React Server Connect Success!!');
});
그리고 화면이 나와야하니 App.js에
import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import List from './component/List';
import Detail from './component/Detail';
import Create from './component/Create';
import Modify from './component/Modify';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path='/' element={<List />} />
<Route path='/list' element={<List />} />
<Route path='/detail/:id' element={<Detail/>} />
<Route path='/create' element={<Create />} />
<Route path='/modify/:id' element={<Modify/>} />
</Routes>
</BrowserRouter>
);
}
export default App;
화면이나오도록 , 누르면 페이지가 넘어 갈 수 있도록 코드를 짜준다.
게시글 목록 페이지 List 구현하기
일단 게시글 목록페이지에서 구현할 기능은 검색기능, 페이지네이션, 최신/조회순 정렬 크게 3가지이다.
return (
<div className='list'>
<Link style={{ textDecoration: "none"}} to={'/list'}><h2>자유게시판</h2></Link>
<hr />
<table>
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>작성일</th>
<th>조회</th>
</tr>
</thead>
<tbody>
{
currentItems.map(b => (
<tr key={b.id} >
<td>{b.id}</td>
<td><Link to={`/detail/${b.id}`} onClick={() => plusViewCount(b.id)}>{b.title}</Link></td>
<td>{b.writer}</td>
<td>{b.reg_date.substring(0,10)}</td>
<td>{b.viewCount}</td>
</tr>
))
}
</tbody>
</table>
)
일단 화면에 표시할 항목들을 적어주고
const [ list, setList ] = useState([]);
const getListData = async () => {
const lists = await axios.get('/list');
setList(lists.data);
}
useEffect(() => {
getListData();
},[]);
app.get('/list', (req, res) => {
console.log('/list');
const sql = 'select * from free_board order by id desc';
db.query(sql, (err, data) => {
if(!err){
res.send(data);
} else {
console.log(err);
res.send('전송오류');
}
})
});
를 통해 데이터를 가져올 수 있게한다.
가져올 수 있게하기 위해 DB는 미리 만들어두었다
db의 구성은 이러하다.
create table free_board (
id bigint auto_increment primary key,
title varchar(30),
contents varchar(500),
writer varchar(20),
reg_date timestamp DEFAULT now(),
viewCount int default 0
);
리스트를 만들면서 Create 전에 데이터를 확인할 수 있게 데이터를 몇개 넣어두자.
이제
app.post('/list/:id', async (req, res) => {
const { id } = req.params;
const sql = `update free_board set viewCount = viewCount + 1 where id = ${id}`;
db.query(sql, (err, data) => {
if(!err){
res.sendStatus(200);
} else {
console.log(err);
res.send('전송오류');
}
})
});
를 통해 조회수가 상세페이지로 넘어가면서 +1 올라가도록 만들어주고.
검색기능을 구현해보도록하자.
const [searchTerm, setSearchTerm] = useState('');
const [filteredList, setFilteredList] = useState([]);
const [searchOption, setSearchOption] = useState('title');
크게 3개의 상태를 정의하는데
검색입력창의 상태, 검색하고 필터링된 리스트의 상태, 그리고 무엇으로 검색할건지의 대한 옵션의 상태를 정의해준다.
const handleSearch = () => {
if (searchTerm) {
const filtered = list.filter(item => {
if (searchOption === 'title') {
return item.title.includes(searchTerm);
} else if (searchOption === 'writer') {
return item.writer.includes(searchTerm);
} else if (searchOption === 'both') {
return item.title.includes(searchTerm) || item.writer.includes(searchTerm);
}
return false;
});
setFilteredList(filtered);
} else {
setFilteredList(list);
}
setSearchTerm('');
};
정의 후 옵션의 상태에따라 포함 유무에따라 필터링 할 수 있도록 이렇게 짜준 후, 검색 후에 내용이 비워지도록 설정.
const getListData = async () => {
const lists = await axios.get('/list');
setList(lists.data);
setFilteredList(lists.data);
}
데이터를 가져올 때도 setFilteredList에도 가져오도록 추가설정.
const handleKeyPress = (event) => {
if (event.key === 'Enter') {
handleSearch();
}
};
엔터를 눌렀을 때도 검색이 가능하도록 추가설정해준다.
<select value={searchOption} onChange={(e) => setSearchOption(e.target.value)}>
<option value="title">제목</option>
<option value="writer">작성자</option>
<option value="both">제목+작성자</option>
</select>
<input
className='search'
type="text"
placeholder="검색어 입력"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
onKeyDown={handleKeyPress}
/>
<button className='searchBtn' onClick={handleSearch}>검색</button>
그리고 화면에 나오도록 이렇게 구현해주면 검색기능이 구현된다.
이제 게시글이 너무많으면 페이지가 생성되어 넘어갈 수 있도록 구현해보자.
이번에는 8개가 넘어가면 페이지가 생성되고 넘어가도록 구현해 보도록 하겠다.
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 8;
페이지의 상태와 한페이지 당 게시글의 수를 정의주도록 한다.
검색기능 구현에 setCurrentPage(1); 를 넣어서 검색 후 1페이지가 되도록 추가해주고.
const totalPages = Math.ceil(filteredList.length / itemsPerPage);
const startIndex = (currentPage - 1) * itemsPerPage;
const currentItems = filteredList.slice(startIndex, startIndex + itemsPerPage);
전체페이지, 현재페이지, 시작인덱스를 정의해주도록 하자.
const handlePageChange = (pageNumber) => {
setCurrentPage(pageNumber);
};
const onPre = () => {
if (currentPage > 1) {
setCurrentPage(currentPage - 1);
}
};
const onNext = () => {
if (currentPage < totalPages) {
setCurrentPage(currentPage + 1);
}
};
그러고 페이지 이동을 구현.
<div className='pagination'>
<button onClick={onPre} disabled={currentPage === 1}>
이전
</button>
{Array.from({ length: totalPages }, (_, index) => (
<button
key={index + 1}
onClick={() => handlePageChange(index + 1)}
disabled={currentPage === index + 1}
>
{index + 1}
</button>
))}
<button onClick={onNext} disabled={currentPage === totalPages}>
다음
</button>
선택한 페이지와 1은 이전, 최대페이지면 다음 버튼을 disabled 해서 비활성화 시켜주면서 화면에 구성하면
페이지네이션이 구현된다.
이제 정렬 기능만 넣으면 리스트 기능은 구현은 끝이난다.
const [sort, setSort] = useState('latest');
정렬 상태를 일단 정의해주고
const sortList = (list) => {
return [...list].sort((a, b) => {
if (sort === 'latest') {
return new Date(b.reg_date) - new Date(a.reg_date);
} else if (sort === 'views') {
return b.viewCount - a.viewCount;
}
return 0;
});
};
sortList 기존 list를 스프레드로 받아와서 sort의 상태에따라 시간순, 조회순으로 정렬 할 수있도록 기능을 만들어준다.
useEffect(() => {
const sortedList = sortList(filteredList);
setFilteredList(sortedList);
}, [sort]);
sort가 바뀔때마다 필터링 되어서 정렬 할 수있도록 useEffect해준다.
<div className='sort-container'>
<div className='sort'>
<button
className={sort === 'latest' ? 'active' : ''}
onClick={() => setSort('latest')}
>
최신순
</button>
<button
className={sort === 'views' ? 'active' : ''}
onClick={() => setSort('views')}
>
조회순
</button>
</div>
</div>
이제 화면에서 최신순 , 조회순 버튼을 만들어주고 active 되면 버튼에 불이 들어오도록 css할 수 있게 active를 넣어주었다.
리스트 페이지 완성
페이지에 따라 게시글 목록이 넘어가고 검색기능, 최신/ 조회순으로 정렬 할 수 있는 기능이 구현된 것을 볼 수 있다.
'React 수업 정리' 카테고리의 다른 글
게시판 구현해보기(3) (1) | 2024.09.24 |
---|---|
게시판 구현해보기(2) (0) | 2024.09.24 |
Board 만들어보기 (0) | 2024.09.19 |
React 5일차 (0) | 2024.09.10 |
React 4일차. (3) | 2024.09.09 |