React, Node.js, MySQL을 이용한 게시판 기능 - 기본설정

이시원·2022년 8월 11일
post-thumbnail

환경 구축

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>
  );

profile
코딩공부

0개의 댓글