react + php + mysql

이강민·2022년 12월 27일
1

react+php

목록 보기
2/4
post-thumbnail

db 통신

이전 글에서 소개했듯이 저자는 카페24의 10GB광망 호스팅서비스를 사용하고 있으며 해당 서비스는 데이터베이스를 제공하는데 데이터베이스 언어로 mariaDB와 mysql을 제공한다. 저자는 mysql을 통해서 php로 데이터베이스를 통신하고 리액트를 통해 가져오고 저장하는 연습을 할 것이다.

ftp서버에 파일 저장

보통은 db만 통신하는 것이 아니라 게시글에서 파일과 이미지를 저장한다. 따라서 ftp를 통해 파일을 올리고 사용자가 입력한 정보는 데이터베이스에 저장하고 저장한 파일의 경로정보만 데이터베이스로 전달하여 ftp에 저장된 파일을 뷰단에서 불러와 볼 것이다.

db와 통신하는 방법

db정보 입력

먼저 db와 통신하려면 정보를 저장해야한다.
php에서 제공하는 mysqli_connect함수를 통해 서버네임과 아이디, 비밀번호, dbname까지 접속 정보를 입력해야한다.
카페24에서 가져오는 정보는 서버컴퓨터의 localhost임으로 서버네임은 localhost로 사용한다.

dbconn.php
<?php
/* 서버 접속 */
$servername = "localhost";
$user = "아이디";
$password = "비밀번호";
$dbname = "db네임";
$connect = mysqli_connect($servername, $user, $password, $dbname);

/* 서버 접속 확인 */
if (!$connect) {
  die("서버와의 연결 실패!: " . mysqli_connect_error());
}
?>

insert

먼저 데이터베이스에 putty나 카페24에서 제공하는 phpAdmin을 통하여 테이블을 생성하자. 이후 우리가 만든 테이블에 사용자가 입력한 정보를 저장할 것이다.
db정보와 사용자 정보를 가져와 insert문을 작성한다.
사용자정보는 post방식으로 전달받고 테이블 형식에 따라 입력폼에 입력하게 된다.

insertData.php
<?php
include_once "dbconn.php";
$boardId = $_POST['boardId'];
$title = $_POST['title'];
$content = $_POST['content'];
$fileUrl = $_POST['fileUrl'];
$date = $_POST['date'];
mysqli_query($connect, "INSERT INTO board (id, title, content, fileUrl, readCnt, date) VALUES ('$boardId', '$title', '$content', '$fileUrl', 0, '$date');")
    ?>

ftp 파일저장

데이터베이스만 통신할 뿐아니라 사용자는 미디어파일도 저장하고 싶을 것이다. 사용자가 파일을 입력하면 파일은 ftp 서버로 보내주고 우리는 이 파일을 찾아올 경로만 db에 저장하면 된다.
ftp 서버도 마찬가지로 접속정보가 필요하고 서버컴퓨터에 저장할 경로를 입력하고 입력받은 파일을 ftp_put 함수를 이용하여 저장하면 된다.

fileSender.php
<?php
//접속정보는 따로 분리하여 관리해야 되지만 여기서는 하나로 함침.
$ftp_server = 'www.kangmin.shop';
$ftp_port = 21;
$ftp_user_name = '아이디';
$ftp_user_pass = '비밀번호';
$filename = $_POST['fileName'];
$fileImg = $_POST['fileImg'];
//실제 저장되는 파일
$localfile = $_FILES['userfile']['tmp_name'];
//이미지 여부에 따라 다른 경로에 파일을 저장한다.
if ($fileImg) {
    $uploaddir = './data/images/';
} else {
    $uploaddir = './data/files/';
}

$ftp_remote_file = $uploaddir . $filename;
// B 호스트에 저장될 실제 파일
$tmpfile = md5("habony_" . $_FILES['userfile']['tmp_name']);
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 서버에 파일 전송
    if (ftp_put($conn_id, $ftp_remote_file, $localfile, FTP_BINARY)) {
        print_r("파일 전송 (ftp) -> UPLOAD 성공");
    } else {
        // FTP서버 파일 전송 실패한 경우 Exception 처리
        throw new Exception("파일 전송 (ftp:" . $conn_id . "," . $ftp_remote_file . "," . $localfile . ") -> UPLOAD 실패");
    }

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

} catch (Exception $e) {

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

view단에서 사용

이제 리액트에서 사용할 차례이다.
이전 글에서 마찬가지로 php와 통신하는 방법은 fetch를 통해서 하거나 axios를 통해서 하면 된다. 여기서는 자바스크립트 기본제공 api인 fetch함수를 통해 post방식으로 php서버에 정보를 넘겨주고 php가 실행되면서 우리가 원하는 mysql과 ftp서버에 접속할 수 있게 된다.
편의상 서버와 접속하는 로직은 하나의 파일로 모았다.

import React from "react";
import { useEffect } from "react";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { v4 as uuidv4 } from "uuid";
import moment from "moment";
import imageCompression from "browser-image-compression";

export default function Sql() {
  const navigate = useNavigate();
  const [id, setId] = useState("");
  const [now, setNow] = useState("");
  const [file, setFile] = useState("");
  const [fileName, setFileName] = useState("");
  const [fileUrl, setFileUrl] = useState("");
  const fileSetting = (e) => {
    setFile(e.target.files[0]);
  };
  useEffect(() => {
    //파일의 고유 이름을 부여하기 위한 라이브러리 사용
    setId(uuidv4());
    //우리가 원하는 형식의 date를 저장하기 위한 포멧
    setNow(moment().format("YYYYMMDD HH:mm:ss"));
  }, []);
  useEffect(() => {
    //파일의 고유이름 + 확장자를 붙여 이름을 완성한다.
    const arr = file?.name?.split(".");
    arr && setFileName(id + "." + arr[1]);
  }, [file, id]);
  useEffect(() => {
    //이미지 파일을 따로 처리하기 위한 처리 
    if (file?.type?.includes("image")) {
      setFileUrl("/data/images/" + fileName);
    } else {
      setFileUrl("/data/files/" + fileName);
    }
  }, [fileName, file.type]);
  const handleSubmit = (e) => {
    e.preventDefault();
    let formElem = document.getElementById("formdata");
    //sql을 사용하는 php와 통신
    fetch("/insertData.php", {
      method: "POST",
      body: new FormData(formElem),
    })
      .then((res) => {
        console.log(res);
        if (res.ok === false) {
          alert("연결이 원활하지 않습니다.");
          navigate("/");
        } else if (res.ok) {
          alert("저장되었습니다.");
          navigate("/");
        }
      })
      .catch(() => {
        console.log("초기 화면 돌아가기");
      });
    const options = {
      maxSizeMB: 0.2,
      maxWidthOrHeight: 800,
      useWebWorker: true,
    };

    const formData = new FormData();
    if (file.type.includes("image")) {
      //이미지파일을 압축하여 저장하기 위한 라이브러리 사용, blob을 리턴한다.
      imageCompression(file, options)
        .then((v) => {
        //blob을 리턴하여 정보들은 따로 FormData객체에 담는다.
          formData.append("userfile", v);
          formData.append("fileImg", v.name);
          formData.append("fileName", fileName);
        })
        .then(() => {
          console.log(formData);
          fetch("/filesender.php", {
            method: "POST",
            body: formData,
          })
            .then((RES) => {
              console.log(RES);
            })
            .catch(() => {
              console.log("초기 화면 돌아가기");
            });
        });
    } else {
      // 이미지가 아닐경우 처리
      formData.append("userfile", file);
      formData.append("fileName", fileName);
      fetch("/filesender.php", {
        method: "POST",
        body: formData,
      })
        .then((RES) => {
          console.log(RES);
        })
        .catch(() => {
          console.log("초기 화면 돌아가기");
        });
    }
  };
  return (
    <div>
      <form onSubmit={handleSubmit} id="formdata">
        <p>
          <input type="text" name="boardId" value={id} hidden />
        </p>
        <p>
          <input type="text" name="date" value={now} placeholder="작성 일" />
        </p>
        <p>
          <input type="text" name="fileUrl" value={fileUrl} hidden />
        </p>
        <p>
          <input type="text" name="title" placeholder="제목" required />
        </p>
        <p>
          <textarea name="content" required></textarea>
        </p>
        <p>
            //파일 용량을 제한하여 ftp 과부하를 방지한다.
          <input type="hidden" name="MAX_FILE_SIZE" value="300000000" />
          <input
            id="userfile"
            name="userfile"
            type="file"
            accept="*"
            onChange={fileSetting}
            required
          />
        </p>
        <p>
          <input type="submit" value={"보내기"} />
        </p>
      </form>
    </div>
  );
}

서로 다른 php와 2번 통신하는데 이때 우리가 원하는 경로를 따로 만들어 저장하기 때문에 서로 다른 php의 저장순서는 신경쓰지 않아도 된다.
다만, 실패했을 때 처리는 반드시 하자.

profile
AllTimeDevelop

0개의 댓글