관리 메뉴

프로그래밍 삽질 중

React로 이미지가 포함된 정보 server에 보내기 본문

과거 프로그래밍 자료들/React

React로 이미지가 포함된 정보 server에 보내기

평부 2022. 5. 20. 23:28

* '따라하며 배우는 노드, 리액트 시리즈 - 쇼핑몰 사이트 만들기' 강의 참고

* react -> node.js로 이미지 및 정보 저장하기

* mongoDB에 저장하는 것까지 확인

 

[Client] - 연결 루트는 "localhost:5000/api/data/image"(server) / "localhost:3000/review/data"(client)

1. App.js

- /review/upload로 페이지 확인하도록 만들기

 

(auth부분은 https://ba-gotocode131.tistory.com/category/%EA%B0%9C%EC%9D%B8%EA%B3%B5%EB%B6%80/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8)에서 확인

 

2.  Review/DataUpload.js

- 전반적인 upload 관련 내용  

 

3. Review/FileUpload.js

- 이미지 저장 관련 내용

 

[Server]

1. app.js(일반적으로 index.js)

- router 연결

 

2. Models/Data.js

- 스키마를 제어하는 파일

 

3. routes/data.js

- 클라이언트에서 받아온 정보를 제어, 저장

- 저장장소 : review폴더(Client, Server와 다른 폴더에 존재)

 

 

 

[1. 설정하기]

 

[Server] - 연결 루트는 "localhost:5000/api/data/image"(server) / "localhost:3000/review/data"(client)

1. app.js(일반적으로 index.js)

- router 연결

 

const express = require("express");
const app = express();
const bodyParser = require("body-parser");
require("dotenv").config();
const cookieParser = require("cookie-parser");
const config_conn = require("./config/key");
const mongoose = require("mongoose");

const PORT = process.env.PORT;

app.listen(PORT, () => {
  console.log("서버 가동");
});

mongoose
  .connect(config_conn.mongoURI, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  })
  .then(() => console.log("MongoDB Connected"))
  .catch((err) => console.log(err));

//body-parser
//application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
//application/json
app.use(bodyParser.json());
app.use(cookieParser());

//이 부분 추가
app.use("/api/data", require("./routes/data"));
app.use("/uploadReview", express.static("uploadReview"));

module.exports = app;

 

 

2. Models/Data.js

- 스키마를 제어하는 파일

 

const mongoose = require("mongoose");

const dataSchema = mongoose.Schema(
  {
    title: {
      type: String,
    },
    description: {
      type: String,
      maxlength: 50,
    },
    images: {
      type: Array,
      default: [],
    },
    //시간 자동 업데이트
  },
  { timestamps: true }
);

const Data = mongoose.model("Data", dataSchema);

module.exports = { Data };

 

 

3. routes/data.js

- 클라이언트에서 받아온 정보를 제어, 저장

- 저장장소 : review폴더(Client, Server와 다른 폴더에 존재)

 

const express = require("express");
const router = express.Router();
const multer = require("multer");
const { Data } = require("../models/review/Data");

var storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "review/");
  },

  filename: function (req, file, cb) {
    cb(null, `${Date.now()}_${file.originalname}`);
  },
});

var upload = multer({ storage: storage }).single("file");

router.post("/image", (req, res) => {
  // 가져온 이미지를 저장을 해주면 된다.

  upload(req, res, (err) => {
    if (err) {
      return req.json({ success: false, err });
    }

    return res.json({
      success: true,
      filePath: res.req.file.path,
      fileName: res.req.file.filename,
    });
  });
});

module.exports = router;

 

 

[Client] - 연결 루트는 "localhost:3000/review/data"

1. App.js

- /review/upload로 페이지 확인하도록 만들기

 

import { BrowserRouter, Routes, Route } from "react-router-dom";
import Auth from "./hoc/auth";
import DataUpload from "./components/views/Review/DataUpload";

function App() {
  return (
    <BrowserRouter>
      <NavBar />
      <Routes>
        <Route path="/review/data" element={Auth(DataUpload, true)} />
      </Routes>
    </BrowserRouter>
  );
}
export default App;

 

 

2.  Review/DataUpload.js

- 전반적인 upload 관련 내용 

 

import React from "react";
import { Button, Form, Input } from "antd";
// import axios from "axios";
// import { useNavigate } from "react-router-dom";
import FileUpload from "./FileUpload";

const { TextArea } = Input;

function DataUpload() {
  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        width: "100%",
        height: "100vh",
      }}
    >
      <div style={{ textAlign: "center", marginBottom: "2rem" }}>
        <br />
        <br />
        <br />
        <h2>계절 관련 업로드</h2>
        <Form>
          <FileUpload />
          <label>이름</label>
          <Input />
          <br />
          <br />
          <label>설명</label>
          <TextArea />
          <br />
          <br />
          <Button>확인</Button>
        </Form>
      </div>
    </div>
  );
}

export default DataUpload;

 

* 결과

 

 

3. Review/FileUpload.js

- 이미지 저장 관련 내용

 

import React, { useState } from "react";
import Dropzone from "react-dropzone";
import { PlusOutlined } from "@ant-design/icons";
import axios from "axios";

function FileUpload(props) {
  const [Images, setImages] = useState([]);

  const dropHandler = (files) => {
    let formData = new FormData();

    const config = {
      header: { "content-type": "multipart/form-data" },
    };

    formData.append("file", files[0]);

    axios
      .post("/api/data/image", formData, config)

      .then((response) => {
        if (response.data.success) {
          //console.log(response.data);
          setImages([...Images, response.data.filePath]);
        } else {
          alert("파일을 저장하는데 실패했습니다.");
        }
      });
  };

  const deleteHandler = (image) => {
    const currentIndex = Images.indexOf(image);

    let newImages = [...Images];
    newImages.splice(currentIndex, 1); //현재인덱스부터 다음 인덱스까지
    setImages(newImages);
  };

  return (
    <div style={{ display: "flex", justifyContent: "space-between" }}>
      <Dropzone onDrop={dropHandler}>
        {({ getRootProps, getInputProps }) => (
          <section>
            <div
              style={{
                width: 300,
                height: 240,
                border: "1px solid lightgray",

                display: "flex",
                alignItems: "center",
                justifyContent: "center",
              }}
              {...getRootProps()}
            >
              <input {...getInputProps()} />

              <PlusOutlined
                style={{
                  fontSize: "3rem",
                  display: "flex",
                  alignSelf: "center",
                }}
              />
            </div>
          </section>
        )}
      </Dropzone>

      <div
        style={{
          display: "flex",
          width: "350px",
          height: "240px",
          overflowX: "scroll",
        }}
      >
        {Images.map((image, index) => {
          return (
            <div onClick={() => deleteHandler(image)} key={index}>
              <img
                style={{ minWidth: "300px", width: "300px", height: "240px" }}
                src={`http://localhost:5000/${image}`}
              />
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default FileUpload;

 

 

 

* 결과

 

[2. 클라이언트-서버에 data 보내기]

 

1. [Client]Review/DataUpload.js

- DataUpload에 Image 정보 보내기

 

import React, { useState } from "react";
import { Button, Form, Input } from "antd";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import FileUpload from "./FileUpload";

const { TextArea } = Input;

function DataUpload() {
  const [Title, setTitle] = useState("");
  const [Description, setDescription] = useState("");
  const [Images, setImages] = useState([]);

  const navigate = useNavigate();

  const titleChangeHandler = (event) => {
    setTitle(event.currentTarget.value);
  };

  const descriptionChangeHandler = (event) => {
    setDescription(event.currentTarget.value);
  };

  //추가1 : FileUpload의 값 받아오도록 함
  const updateImages = (newImages) => {
    setImages(newImages);
  };

  //추가2
  const submitHandler = (event) => {
    //페이지 자동 리프레시 되는 것 막음
    event.preventDefault();

    //값이 하나라도 비면 오류
    if (!Title || !Description || !Images) {
      return alert("모든 값을 작성해야 합니다.");
    }

    //값을 다 채우면 서버 request로 보낸다
    const body = {
      //로그인된 사람의 아이디
      title: Title,
      description: Description,
      images: Images,
    };

    axios.post("/api/data", body).then((response) => {
      if (response.data.success) {
        alert("상품 업로드에 성공했습니다.");
        navigate("/");
      } else {
        alert("상품 업로드에 실패했습니다.");
      }
    });
  };

  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        width: "100%",
        height: "100vh",
      }}
    >
      <div style={{ textAlign: "center", marginBottom: "2rem" }}>
        <br />
        <br />
        <br />
        <h2>계절 관련 업로드</h2>
        <Form onSubmitCapture={submitHandler}>
          {/* FileUpload에서 refreshFunction으로 제어 */}
          <FileUpload refreshFunction={updateImages}/>
          <label>이름</label>
          <Input onChange={titleChangeHandler} value={Title} />
          <br />
          <br />
          <label onChange={descriptionChangeHandler} value={Description}>
            설명
          </label>
          <TextArea />
          <br />
          <br />
          <Button onClick={submitHandler}>확인</Button>
        </Form>
      </div>
    </div>
  );
}

export default DataUpload;

 

3. [Client]Review/FileUpload.js

- DataUpload로 정보 보내기

 

import React, { useState } from "react";
import Dropzone from "react-dropzone";
import { PlusOutlined } from "@ant-design/icons";
import axios from "axios";

function FileUpload(props) {
  const [Images, setImages] = useState([]);

  const dropHandler = (files) => {
    let formData = new FormData();

    const config = {
      header: { "content-type": "multipart/form-data" },
    };

    formData.append("file", files[0]);

    axios
      .post("/api/data/image", formData, config)

      .then((response) => {
        if (response.data.success) {
          //console.log(response.data);
          setImages([...Images, response.data.filePath]);
          //추가
          props.refreshFunction([...Images, response.data.filePath]);
        } else {
          alert("파일을 저장하는데 실패했습니다.");
        }
      });
  };

  const deleteHandler = (image) => {
    const currentIndex = Images.indexOf(image);

    let newImages = [...Images];
    newImages.splice(currentIndex, 1); //현재인덱스부터 다음 인덱스까지
    setImages(newImages);
    //추가
    props.refreshFunction(newImages);
  };

  return (
    <div style={{ display: "flex", justifyContent: "space-between" }}>
      <Dropzone onDrop={dropHandler}>
        {({ getRootProps, getInputProps }) => (
          <section>
            <div
              style={{
                width: 300,
                height: 240,
                border: "1px solid lightgray",

                display: "flex",
                alignItems: "center",
                justifyContent: "center",
              }}
              {...getRootProps()}
            >
              <input {...getInputProps()} />

              <PlusOutlined
                style={{
                  fontSize: "3rem",
                  display: "flex",
                  alignSelf: "center",
                }}
              />
            </div>
          </section>
        )}
      </Dropzone>

      <div
        style={{
          display: "flex",
          width: "350px",
          height: "240px",
          overflowX: "scroll",
        }}
      >
        {Images.map((image, index) => {
          return (
            <div onClick={() => deleteHandler(image)} key={index}>
              <img
                style={{ minWidth: "300px", width: "300px", height: "240px" }}
                src={`http://localhost:5000/${image}`}
              />
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default FileUpload;

 

3. [Server]/routes/data.js

 

const express = require("express");
const router = express.Router();
const multer = require("multer");
const { Data } = require("../models/review/Data");

var storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "uploadReview/");
  },

  filename: function (req, file, cb) {
    cb(null, `${Date.now()}_${file.originalname}`);
  },
});

var upload = multer({ storage: storage }).single("file");

router.post("/image", (req, res) => {
  // 가져온 이미지를 저장을 해주면 된다.
  upload(req, res, (err) => {
    if (err) {
      return req.json({ success: false, err });
    }

    return res.json({
      success: true,
      filePath: res.req.file.path,
      fileName: res.req.file.filename,
    });
  });
});

//client로 가져온 정보 저장
router.post("/", (req, res) => {
  //받아온 정보를 DB에 저장함
  const data = new Data(req.body);

  data.save((err) => {
    if (err) return res.status(400).json({ success: false, err });
    return res.status(200).json({ success: true });
  });
});

module.exports = router;

 

 

* 최종 결과