
환경 구축
yarn create react-app react_board_ex01
yarn add axios express body-parser mysql cors
yarn add nodemon
package.json

npm run dev
데이터베이스 연결
index.js
<script>
const express = require("express"); //웹서버 생성
const mysql = require("mysql"); // 데이터베이스
const bodyParser = require("body-parser"); // 요청정보 처리
const cors = require("cors"); // 교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)
const app = express(); // 익스프레스 설정
const PORT = process.env.port || 8008; // 포트번호 설정 포트번호는 0부터 16비트
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: true })); // 객체 형식 처리
let corsOptions = {
origin: "*", // 출처 허용 옵션
credential: true, // 사용자 인증이 필요한 리소스(쿠키, ...등) 접근
};
app.use(cors(corsOptions)); // 미들웨어 설정 작업
const db = mysql.createPool({
// mysql 연결 설정
host: "localhost",
user: "root",
password: "123456",
database: "bbbs",
// port번호 생략하면 기본값 3306 지정되어있음
});
</script>
app.js
<script>
function App() {
// 게시판 글 저장
const [boardlist, setBoardlist] = useState({ boardList: [] });
// 상세 글, 데이터 수정할 때 글 불러오기
const [article, setArticle] = useState({
// 테이블 구조
board_num: 0,
board_writer: "",
board_title: "",
board_content: "",
board_date: "",
});
// 0 : 글쓰기, 1 : 상세보기, 2 : 글수정
const [actionMode, setActionMode] = useState({ mode: 0 });
</script>
글 목록 함수
<script>
// 글목록
const getList = () => {
// alert("getList(actionMode) =>" + actionMode.mode);
// Ajax 구현
axios
.get("http://localhost:8008/list", {}) // {} 빈 객체 전달
// index.js에 있는 result를 res가 전달 받음
.then((res) => {
// res에 뭐가 들어있는지 확인하고 싶으면 콘솔로그 찍기
console.log(`res=> ${res}`);
const { data } = res;
console.log("data ==>", data);
setBoardlist({
// data를 boardList에 저장
// 상태값이 바뀌면 리렌더링이 되면서 목록이 나타남
boardList: data,
});
setActionMode({
// ...actionMode,
mode: 0, // 글쓰기
});
})
.catch((e) => {
console.error(e);
});
};
</script>
<script>
app.get("/list", (req, res) => {
console.log("list!!!");
const sqlQuery =
"SELECT BOARD_NUM, BOARD_WRITER, BOARD_TITLE, BOARD_CONTENT, DATE_FORMAT(BOARD_DATE, '%Y-%m-%d') AS BOARD_DATE FROM BOARD_TBL;";
db.query(sqlQuery, (err, result) => {
// select문 결과를 클라이언트에게 반환
res.send(result);
});
});
</script>


상세보기 함수
<script>
const handleDetail = (e) => {
// alert("handleDetail(actionMode) =>" + actionMode.mode);
axios
.post("http://localhost:8008/detail", { num: e.target.id }) // e: 이벤트 객체, id={article.BOARD_NUM} // id를 num에 담아 /detail에게 post 방식으로 객체로 전달
.then((res) => {
// res를 비구조화 할당 방식을 data에 담음,데이터는 딱 하나 들어있음
const { data } = res;
console.log("detail =>", data);
// 데이터가 있으면
if (res.data.length > 0) {
setArticle({
// article 상태객체: 직접변경 x -> ...를 통해 복제
...article,
// 데이터가 하나밖에 없으니까 data[0]
board_num: data[0].BOARD_NUM, // key가 대문자로 되어 있어서 대문자로 써야함
board_writer: data[0].BOARD_WRITER,
board_title: data[0].BOARD_TITLE,
board_content: data[0].BOARD_CONTENT,
board_date: data[0].BOARD_DATE,
});
setActionMode({
// 상세보기위해 모드를 변경
// 리렌더링이 일어나면서 app.js 실행될 때 모드가 변경되어있음
...actionMode,
mode: 1, // 상세보기
});
}
})
.catch((e) => {
console.error(e);
});
};
</script>
<script>
app.post("/detail", (req, res) => {
// req.body 안에 num 담겨있음
console.log("/detail", req.body);
var num = parseInt(req.body.num);
const sqlQuery =
"SELECT BOARD_NUM, BOARD_WRITER, BOARD_TITLE, BOARD_CONTENT, DATE_FORMAT(BOARD_DATE,'%Y-%m-%d') AS BOARD_DATE FROM BOARD_TBL where BOARD_NUM = ?;";
// [num]이 ?에 들어감
db.query(sqlQuery, [num], (err, result) => {
// 그 결과가 result에 담기고 app에 res에게 전달
res.send(result);
});
});
</script>
글 수정 폼 함수
<script>
const handleUpdateForm = (e) => {
alert(
"handleUpdateForm(actionMode) =>" + actionMode.mode + ", " + e.target.id
);
axios
.post("http://localhost:8008/detail", { num: e.target.id })
.then((res) => {
const { data } = res;
console.log("handleUpdateForm =>", data);
if (res.data.length > 0) {
setArticle({
...article,
board_num: data[0].BOARD_NUM,
board_writer: data[0].BOARD_WRITER,
board_title: data[0].BOARD_TITLE,
board_content: data[0].BOARD_CONTENT,
board_date: data[0].BOARD_DATE,
});
setActionMode({
// 글수정위해 모드를 변경
// 리렌더링이 일어나면서 app.js 실행될 때 모드가 변경되어있음
...actionMode,
mode: 2, // 글수정하기
});
}
})
.catch((e) => {
console.error(e);
});
};
</script>
<script>
app.post("/detail", (req, res) => {
// req.body 안에 num 담겨있음
console.log("/detail", req.body);
var num = parseInt(req.body.num);
const sqlQuery =
"SELECT BOARD_NUM, BOARD_WRITER, BOARD_TITLE, BOARD_CONTENT, DATE_FORMAT(BOARD_DATE,'%Y-%m-%d') AS BOARD_DATE FROM BOARD_TBL where BOARD_NUM = ?;";
// [num]이 ?에 들어감
db.query(sqlQuery, [num], (err, result) => {
// 그 결과가 result에 담기고 app에 res에게 전달
res.send(result);
});
});
</script>
글 수정 함수
<script>
const handleUpdate = () => {
console.log("handleUpdate =>", article);
axios
.post("http://localhost:8008/update", {
article: article, // article을 post방식으로 /update에 전달
})
/* 그냥 예시임
.then((res) =? {
console.log("handleUpdate(changedRows) =>", res.data.changedRows)
}) */
.then(() => {
getList();
})
.catch((e) => {
console.error(e);
});
};
</script>
<script>
app.post("/update", (req, res) => {
console.log("/update", req.body); // req.body안에 article 객체 들어있음
// 글번호를 읽어와서 타이틀과 콘텐츠 변경
var title = req.body.article.board_title;
var content = req.body.article.board_content;
var num = req.body.article.board_num;
const sqlQuery =
"update BOARD_TBL set BOARD_TITLE=?, BOARD_CONTENT=?,BOARD_DATE=now() where board_num=?;";
// ?에 title, content, num 파라미터 전달 -> db에 수정이 일어남
db.query(sqlQuery, [title, content, num], (err, result) => {
// result에 아무것도 없음. update는 반환되는 값없음. 그래서 사실 안보내도됨
res.send(result);
console.log("result=", result); // changedRows: 몇 개가 수정되었는지
});
});
</script>
BoardList
<BoardList
boardlist={boardlist}
actionmode={actionMode}
handlelist={getList}
handledetail={handleDetail}
handleupdateform={handleUpdateForm}
>
</BoardList>
<script>
import { useEffect } from "react";
import BoardArticle from "./BoardArticle";
const BoardList = ({
// props 비구조화 할당 방식
boardlist,
actionmode,
handlelist,
handledetail,
handleupdateform,
}) => {
useEffect(() => {
// getList 호출
handlelist();
}, []); // [] 맨처음 랜더링할때 수행, boardlist를 화면에 출력할 때
// getList 호출 이후에
// boardlist에 데이터저장되어있음
// 데이터가 없으면
if (boardlist.boardList.length === 0) {
return (
<div>
<table width="700px" border="1" align="center">
<thead>
<tr>
<th width="60">번호</th>
<th width="240">제목</th>
<th width="100">작성자</th>
<th width="100">작성일</th>
<th width="200">수정/삭제</th>
</tr>
</thead>
</table>
</div>
);
// 데이터가 존재하면
} else {
return (
<div>
<table width="700px" border="1" align="center">
<thead>
<tr>
<th width="60">번호</th>
<th width="240">제목</th>
<th width="100">작성자</th>
<th width="100">작성일</th>
<th width="200">수정/삭제</th>
</tr>
</thead>
<tbody>
{/* boardlist : 배열 */}
{/* map : 가지고있는 요소 개수만큼 반복 */}
{/* article : boardlist 데이터 전달 받음 */}
{/* article를 BoardArticle에 전달 */}
{boardlist.boardList.map((article) => {
return (
<BoardArticle
// actionmode={actionmode}
article={article}
// key={article.board_num}
handlelist={handlelist}
handledetail={handledetail}
handleupdateform={handleupdateform}
/>
);
})}
</tbody>
</table>
</div>
);
}
};
export default BoardList;
</script>
BoardArticle
<script>
import axios from "axios";
const BoardArticle = ({
// key, actionmode는 따로 전달받을 필요없어 뺌
article,
handlelist,
handledetail,
handleupdateform,
}) => {
// 삭제버튼 눌렀을 때
// 삭제하고 끝내면 모드안바꾸고 그냥 끝내면 되니까 여기 배치
const handleDelete = (e) => {
console.log("handleDelete(board_num) =>", e.target.id);
axios
.post("http://localhost:8008/delete", {
num: e.target.id,
})
.then(() => {
handlelist();
})
.catch((e) => {
console.error(e);
});
};
console.log("BoardArticle =>", article);
</script>
<script>
app.post("/delete", (req, res) => {
const num = req.body.num;
console.log("/delete(id) => ", num);
const sqlQuery = "DELETE FROM BOARD_TBL WHERE BOARD_NUM = ?;";
db.query(sqlQuery, [num], (err, result) => {
console.log(err);
res.send(result);
});
});
</script>
<tr>
{/* 번호 */}
<td>{article.BOARD_NUM}</td>
{/* 제목 */}
<td>
<a href="#" id={article.BOARD_NUM} onClick={handledetail}>
{article.BOARD_TITLE}
</a>
</td>
<td>{article.BOARD_WRITER}</td>
<td>{article.BOARD_DATE}</td>
<td align="center">
<input
type="button"
value="수정"
id={article.BOARD_NUM}
onClick={handleupdateform}
></input>
<input
type="button"
value="삭제"
id={article.BOARD_NUM}
onClick={handleDelete}
></input>
</td>
</tr>
);
