본문 바로가기

React.js

React) form-data로 이미지 서버로 전송하기

회원가입을 구현하기 위해 이미지 파일을 백엔드로 전송해야했다

오늘 만들거 디자인


 

이미지를 업로드하고 업로드 성공 시 미리보기 위한 컴포넌트

const ProfileImage = ({ onFileChange }) => {
    const [selectedImage, setSelectedImage] = useState(null);

    const handleImageChange = (event) => {
        const file = event.target.files[0];
    
        if (file) {
          const reader = new FileReader();
    
          reader.onloadend = () => {
            setSelectedImage(reader.result);
            onFileChange(file); // 부모 컴포넌트로 파일 전달 
          };      
        }
      };

    return(
        <Label form="file">
            {selectedImage ? (
                <Image src={selectedImage} alt="선택한 이미지" />
            ) : (
                <>
                    <DefaultImage src={ProfileBasic} alt="기본이미지" />
                </>
            )}
            <StyledInput id="file" type="file" accept="image/*" onChange={handleImageChange} />
        </Label>
    )
}

//styled-components 생략

 

FileReader 라는 객체를 이용해서 파일을 업로드 했다.

 

FileReaderFile, Blob 객체를 사용해 특정 파일을 읽어들여 자바스크립트에서 파일에 접근할 수 있도록 도와주는 도구이다.

onloadend 라는 이벤트 핸들러를 사용했는데, 파일의 읽기 작업이 성공/실패에 관계없이 완료되었을 때 발생한다.

  ( onloadend 파일 읽기가 완료되었을 때 로딩 스피너를 감추는 등의 작업에 사용이 적절)

 

이번 상황에서는 onloadend 보다 onload 사용이 더 적절해 보인다

 (onload 읽기 작업이 성공적으로 완료되었을 때 발생)


 

이미지 업로드를 성공적으로 하고나면

상위 컴포넌트에서 백엔드로 이미지와 JSON 데이터 전송

 

실패했던 코드

    const handleSubmitButton = (event) => {

    event.preventDefault();
    if (true) { //selectedFile
      const formData = new FormData();
      formData.append("image", selectedFile);

      const request = {
          email: "123456@gmail.com",
          password: "1234",
      };

      formData.append("request", JSON.stringify(values));
      
      axios
        .post(`apiURL`,formData , {
          headers: {
            'Content-Type': "multipart/form-data",
          },
        })
        .then(function (response) {
          // 성공적으로 응답 받았을 때의 처리
          console.log("응답 데이터:", response.data);
          navigate("/JudgePage");
        })
        .catch(function (error) {
          // 오류 발생 시의 처리
          console.error("오류 발생:", error);
          console.log(request);
          let values = formData.keys();
          for (const pair of values) {
              console.log(pair,'폼데이터'); 
	 }
        });
    }

  };

콘솔로 찍어봤을 때 데이터가 들어있긴 했는데

'Content-Type': "multipart/form-data" 도 지정해줬는데 이렇게 보내면 백엔드에서 아래 오류가 확인됐다

 

에러코드:

WARN 114209 — [nio-8080-exec7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content-Type 'application/octet-stream' is not supported]


 

FormData는 파일과 텍스트 데이터를 함께 서버로 전송하기 위해서 사용됨

📌오류난 이유📌

FormData는 주로 텍스트 데이터를 다루는데 최적화 되어있기 때문에

단순 JSON 데이터를 FormData로 추가하고 서버로 보내는 경우

를 일반 텍스트 데이터로 처리하게 되어 서버에서 해당 데이터가 JSON임을 인식하지 못함

 

FormData에 추가할 수 있는 데이터 유형은 String,File,Blob, FormData객체 자체,숫자, 불리언 등 기본데이터 라고 한다

 

해결방법

      const json = JSON.stringify(values);
      const blob = new Blob([json], { type: "application/json" });
      formData.append("request", blob);

Blob 객체를 생성해 객체의 타입을 application/json으로 설정해 append 하면 된다

완성된 코드

const SignupInfo = (props) => {
  //로그인 정보 관리
  const [selectedFile, setSelectedFile] = useState(null);
  const [values, setValues] = useState({
    email: "",
    password: "",
  });

  const handleFileChange = (file) => {
    setSelectedFile(file);
  };

  const handleChange = (e) => {
    setValues((prevValues) => ({
      ...prevValues
    }));
  };


  const handleSubmitButton = (event) => {
    //정보들을 서버로 전송

    event.preventDefault();
    if (selectedFile) {
      const formData = new FormData();
      formData.append("image", selectedFile);

      const json = JSON.stringify(values);

      const blob = new Blob([json], { type: "application/json" });
      formData.append("request", blob);

      axios
        .post(`https://umcfriend.kro.kr/api/v1/users`, formData, {
          headers: {
            "Content-Type": "multipart/form-data",
          },
        })
        .then(function (response) {
          // 성공적으로 응답 받았을 때의 처리
        })
        .catch(function (error) {
          // 오류 발생 시의 처리
          console.error("오류 발생:", error);
        });
    }
  };

  return (
    <>
      <Title title="회원가입" />

      <AppContainer>
        <InfoMessage>먼저 프로필 사진을 등록해주세요 &nbsp; <p>*</p> </InfoMessage>
        <LogoContainer>
          <ProfileImage
            onFileChange={handleFileChange}
            selectedFile={selectedFile}
          />
        </LogoContainer>

        <InfoMessage>아이디와 비밀번호를 입력해주세요&nbsp;<p>*</p></InfoMessage>
        <LoginForm onSubmit={handleSubmit}>
          <Input
            type="text"
            name="email"
            value={values.email}
            onChange={handleChange}
            placeholder="이메일"
          />
          <Input
            type="password"
            name="password"
            value={values.password}
            onChange={handleChange}
            placeholder="패스워드"
            autoComplete="new-password"
          />
          <Input
            type="password"
            name="pwck"
            value={values.passwordCheck}
            onBlur={checkWrongPW}
            placeholder="패스워드 확인"
            autoComplete="new-password"
          />

 

 

참고

https://velog.io/@hhhminme/Axios%EC%97%90%EC%84%9C-Post-%EC%8B%9C-Contenttypeapplicationoctet-streamnotsupported-%ED%95%B8%EB%93%A4%EB%A7%81415-%EC%97%90%EB%9F%AC