[회사랜딩페이지]파일선택이 됐을 때 서버로 전송되지 않게 바꾸기 (submit할 때 전송)

piper ·2024년 4월 20일
0

Project

목록 보기
15/15

firebase에 있는 업로드 메서드를 그대로 가져왔다.
현재 파일을 업로드 할 때 문제는 클릭을 해서 파일선택이 되었을 때 firebase의 클라우드서버에 업로드되고 말았다. 그러면 사용자가 테스트로 파일을 선택하고 모든 폼을 입력하지 않고 서비스에서 나갔을 때
괜히 사진만 저장한 상황이 발생한다. 그래서 모든 폼을 다 제출했을 때 서버에 올라가게 바뀌어야 했다.

useEffect(() => {
    const uploadFile = () => {
      const storageRef = ref(storage, file.name);
        (snapshot) => {
          const progress =
            (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
          console.log("Upload is " + progress + "% done");
          setPerc(progress);
          switch (snapshot.state) {
        }
      );
    };
    file && uploadFile();
  }, [file]);

파일의 상태가 바뀔 때마다 uploadfile()이 실행되고 있는데
파일의 상태가 아니라 다른 상태가 바뀔 때로 바꿔주고
uploadfile()이 실행되는 것이 아닌 다른 함수가 최종적으로 실행되게 하는 것이 좋을것같다.

일단 file 대신에 쓸수있는 다른 상태인
아닌 올려도 된다 / 안된다의 상태를 어디서 알려줄수 있을지 생각해본다.
그냥 단순하게 된다가 되면 올라가게 해야겠다.
그래서 이걸 true로 생각한다면
false가 되면 useEffect는 실행되지 않을 것이고 true가 된다면 useEffect가 실행될 것이다.

그럼 submit을 하고 나서 어떤 상태를 true로 바꿔줄 수 있을것이다.
새로운 trigger라는 state를 만들고 전송이 모두 완료가 된 후에
true로 저장해준다.


 const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const response = await setDoc(doc(db, "products", data.id), {
        ...data,
      });
      window.alert("data has been stored in database ! ");
      setData({
        id: uuidv4(),
        title: "",
        content: "",
        category: "",
      });
      setFile("");
    } catch (e) {
      console.log("firebase post error", e);
    if (!file) {
      window.alert("파일을입력하시오");
    }
    setTrigger(true);
  };
  

이제 위의 파이어베이스의 메서드가 있는 useEffect를 file이 아닌 trigger가
감지되면 uploadFile()이 실행되게 하면 된다.

그리고 더 확실히 하기 위해서 try{이하}의 구문을 useEffect 안으로
옮긴다. 그리고 uploadFile이 실행되기 전에
file이 있고 trigger가 ture일 때만 실행되도록 조건을 걸어준다.
이미 trigger는 handlesubmit 버튼을 누를 때 true로 바뀐다.
이왕이면 파일업로드 상태의 숫자가 100이면 실행되게 만들어준다.

이제 useEffect는 trigger가 ture이며 file이 있을 때
trigger 상태가 바뀌면 실행된다.

전체코드는 아래와 같다.

import React, { useState, useEffect, useRef } from "react";
import styled from "styled-components";
import { addDoc, collection, doc, setDoc } from "firebase/firestore";
import { db, storage } from "../../firebase";
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
import { v4 as uuidv4 } from "uuid";
import DeleteData from "./DeleteData";

const AdminDataInput = () => {
  const [data, setData] = useState({
    id: uuidv4(),
    title: "",
    content: "",
    category: "",
  });

  const fileRef = useRef();

  const [file, setFile] = useState("");
  const [perc, setPerc] = useState(null);
  const [trigger, setTrigger] = useState(false);

  const addFirebaseContents = async () => {
    try {
      const response = await setDoc(doc(db, "products", data.id), {
        ...data,
      });
      console.log("data:", data);
      console.log("response", response);
      window.alert("data has been stored in database ! ");
      setData({
        id: uuidv4(),
        title: "",
        content: "",
        category: "",
      });
      setFile(null);
      fileRef.current.value = "";
      setTrigger(true);
    } catch (e) {
      console.log("firebase post error", e);
    }
  };

  useEffect(() => {
    const uploadFile = () => {
      const storageRef = ref(storage, file.name);
      const uploadTask = uploadBytesResumable(storageRef, file);

      uploadTask.on(
        "state_changed",
        (snapshot) => {
          const progress =
            (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
          if (progress === 100) {
            addFirebaseContents();
          }
          console.log("Upload is " + progress + "% done");
          setPerc(progress);
          switch (snapshot.state) {
            case "paused":
              console.log("Upload is paused");
              break;
            case "running":
              console.log("Upload is running");
              break;
            default:
              break;
          }
        },
        (error) => {
          console.log(error);
        },
        () => {
          getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
            console.log("File available at", downloadURL);
            setData((prev) => ({ ...prev, img: downloadURL }));
          });
        }
      );
    };
    if (file && trigger) {
      uploadFile();
    }
  }, [trigger]);

  const handleChange = (e) => {
    const { name, value } = e.target;
    setData((prev) => ({ ...prev, [name]: value }));
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!file) {
      window.alert("파일을입력하시오");
    }
    setTrigger(true);
  };
  console.log(file);
  console.log(trigger);

  return (
    <>
      <Layer>
        <InputLayer>
          <p>fill out the data field below for upload.</p>
          <form onSubmit={handleSubmit}>
            <label>
              title:
              <input
                type="text"
                onChange={handleChange}
                name="title"
                value={data.title}
              />
            </label>
            <label>
              content:
              <input
                type="text"
                onChange={handleChange}
                name="content"
                value={data.content}
              />
            </label>
            <label>
              category:
              <input
                type="text"
                onChange={handleChange}
                name="category"
                value={data.category}
              />
            </label>
            <label>
              img file:
              <input
                type="file"
                onChange={(e) => setFile(e.target.files[0])}
                name="file"
                ref={fileRef}
              />
            </label>
            <span>
              <button disabled={perc !== null && perc < 100}>submit</button>
            </span>
          </form>
        </InputLayer>
      </Layer>
    </>
  );
};

export default AdminDataInput;

const Layer = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: evenly;
`;

const InputLayer = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;

  h2 {
    font-size: 2rem;
    margin-bottom: 10px;
  }

  form {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 15px;
  }

  label {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 5px;
    font-size: 1.2rem;
  }

  input[type="text"],
  input[type="file"] {
    padding: 8px;
    border: 1px solid #ccc;
    border-radius: 4px;
    width: 300px;
  }

  input[type="file"] {
    padding: 8px;
    border: 1px solid #ccc;
    border-radius: 4px;
    width: 300px;
    background-color: #f9f9f9;
  }

  input[type="text"]:focus,
  input[type="file"]:focus {
    outline: none;
    border-color: #007bff;
  }

  button {
    padding: 10px 20px;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s ease;
  }

  button:hover {
    background-color: #0056b3;
  }

  &:disabled {
    background-color: gray;
    cursor: not-allowed;
  }
`;
profile
연습일지

0개의 댓글