react + php + mysql

이강민·2022년 12월 27일

react+php

목록 보기
3/4
post-thumbnail

select

이제 데이터베이스에 저장하였으니 select를 통해 데이터베이스에 저장된 정보를 가져오자. 동일하게 php를 통해서 정보를 가져오면 되며, 가져온 정보를 변수에 담아 json으로 응답하면 된다. 이때 주의사항으로 json을 echo를 통해 응답하는데 만약 json 이외의 다른 정보가 echo 되고 있다면 반드시 제거하거나 따로 처리해야한다.

selectData.php
<?php
//앞서 만든 dbconn 활용
include_once "dbconn.php";
header("Content-Type: application/json");
mysqli_set_charset($con, "utf8");
// 불러오는 데이터는 한정된 데이터를 가져와야 과부하에 걸리지 않는다.
//원래는 가져오는 데이터의 limit 조건을 걸어야 한다.
$res = mysqli_query($connect, "SELECT * FROM `board`;");

$result = array();
// 쿼리문의 결과(res)를 배열형식으로 변환(result) 
while ($row = mysqli_fetch_array($res)) {
  array_push($result, array('id' => $row[0], 'title' => $row[1], 'content' => $row[2], 'fileUrl' => $row[3], 'readCnt' => $row[4], 'date' => $row[5]));
}
// 배열형식의 결과를 json으로 변환 
echo (json_encode(array("result" => $result), JSON_UNESCAPED_UNICODE));
// DB 접속 종료 
mysqli_close($con);
?>

불러온 데이터의 url이 파일인지 이미지에 따라서 서로다른 처리를 진행하거나 저장할때 파일인지 이미지인지 정보를 저장하여 불러올때 구분하면되지만 여기서 편의상 이미지만 처리하고 이미지를 img태그를 활용하여 보여주고 있다.

import React from "react";
import { useState } from "react";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";

export default function Reading() {
  const navigate = useNavigate();
  const [data, setData] = useState([]);
  useEffect(() => {
    //selectData.php를 요청
    fetch("/selectData.php", {
      method: "POST",
    })
      .then((res) => res.json())
      .then((data) => setData(data.result))
      .catch((err) => {
        console.log(err, "초기 화면 돌아가기");
      });
  }, []);

  const readDetilHandle = (data) => {
    //react-router의 navigate를 활요하고 있으면 가져온 data를 전달하고 있다.
    navigate(`/read/${data.id}`, { state: data });
  };

  return (
    <>
      <table>
        <thead>
          <tr>
            <th>날짜</th>
            <th>제목</th>
            <th>조회수</th>
          </tr>
        </thead>
        <tbody>
          {data &&
            data.map((data) => (
              <tr key={data.id}>
                <td>{data.date}</td>
                <td onClick={() => readDetilHandle(data)}>{data.title}</td>
                <td>{data.readCnt}</td>
              </tr>
            ))}
          {data.length === 0 && "게시글이 없습니다."}
        </tbody>
      </table>
    </>
  );
}

delete

수정에 들어가기 앞서 delete를 먼저 짚고 넘어가겠다. 이유는 데이터베이스를 수정할때 파일을 수정하면 ftp에서 기존의 파일을 삭제해야 되기 때문이다.

sql delete

sql에서의 delete는 쿼리문만 변경하면된다.

deleteData.php
<?php
include_once "dbconn.php";
$boardId = $_POST['boardId'];
mysqli_query($connect, "DELETE FROM board WHERE id = '$boardId'")
  ?>

ftp delete

ftp 접속정보를 이용하여 접속하고 저장되어 있는 파일경로를 이용하여 ftp_delete함수에 인자로 전달한다.

<?php
$ftp_server = 'www.kangmin.shop';
$ftp_port = 21;
$ftp_user_name = 'km1416';
$ftp_user_pass = 'Lkm14162535!';
//여기서 사용하는 파일경로는 앞에 /나 .이 없어야 한다.
//예를 들어 /data/files/abcd.hwp 가 아니라 
//예를 들어 data/files/abcd.hwp 가 되어야한다.
$filePath = $_POST['filePath'];
try {

    // FTP서버 접속
    $conn_id = ftp_connect($ftp_server, $ftp_port);

    // FTP서버 접속 실패한 경우 Exception 처리
    if ($conn_id == false) {
        throw new Exception("[IP:" . $ftp_server . ":" . $ftp_port . "] FTP 서버 접속 실패");
    }

    // FTP서버 로그인
    $login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass);

    // FTP서버 로그인 실패한 경우 Exception 처리
    if ($login_result == false) {
        throw new Exception("[IP:" . $ftp_server . ":" . $ftp_port . "], [USER:" . $ftp_user_name . "] 로그인 실패");
    }
    ftp_pasv($conn_id, true);
    // FTP 서버에 파일 전송
    // try to delete file
    if (ftp_delete($conn_id, $filePath)) {
        echo "deleted";
    } else {
        echo "Could not delete";
    }

    // FTP 서버와 연결 끊음
    ftp_close($conn_id);

} catch (Exception $e) {

    // FTP 서버와 연결 끊음
    ftp_close($conn_id);
    print_r($e->getMessage());
}
?>

view단에서 처리

import React from "react";
import { useEffect } from "react";
import { useLocation, useNavigate } from "react-router-dom";

export default function ReadDetail() {
  const navigate = useNavigate();
  const { state: data } = useLocation();
  const deleteHandler = (e) => {
    const formData = new FormData();
    //파일 경로에 /나 .을 제거하기위한 로직
    const filePath = data.fileUrl.substr(1);
    console.log(filePath);
    formData.append("boardId", e.target.id);
    formData.append("filePath", filePath);
    if (window.confirm("정말 삭제하시겠습니까?")) {
      fetch("/deleteData.php", {
        method: "POST",
        body: formData,
      })
        .then(() => {})
        .catch((err) => {
          console.log(err, "초기 화면 돌아가기");
        });
      fetch("/fileDelete.php", {
        method: "POST",
        body: formData,
      })
        .then((RES) => {
          console.log(RES);
        })
        .finally(() => {
          navigate("/read");
        })
        .catch((err) => {
          console.log(err, "초기 화면 돌아가기");
        });
    }
  };
  useEffect(() => {
    const formData = new FormData();
    formData.append("boardId", data.id);
    formData.append("readCnt", Number(data.readCnt) + 1);
    fetch("/updateReadCnt.php", {
      method: "POST",
      body: formData,
    })
      .then((RES) => {
        console.log(RES);
      })
      .catch((err) => {
        console.log(err, "초기 화면 돌아가기");
      });
  }, [data.id, data.readCnt]);

  const editHandler = () => {
    navigate("readEdit", { state: data });
  };
  const listHandlr = () => {
    navigate("/read");
  };
  return (
    <table>
      <thead>
        <tr>
          <th>날짜</th>
          <th>제목</th>
          <th>내용</th>
          <th>조회수</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>{data.date}</td>
          <td>{data.title}</td>
          <td>{data.content}</td>
          <td>{data.readCnt}</td>
        </tr>
        <tr>
          <td colSpan={4}>
            <img src={data.fileUrl} alt="" />
          </td>
          <td></td>
          <td></td>
          <td></td>
        </tr>
      </tbody>
      <tfoot>
        <button onClick={deleteHandler} id={data.id}>
          삭제
        </button>
        <button onClick={editHandler}>수정</button>
        <button onClick={listHandlr}>목록</button>
      </tfoot>
    </table>
  );
}
profile
AllTimeDevelop

0개의 댓글