0ju-log
💻 Frontend

[React] 이미지 업로드 구현하기

💡 시작하기

너무나도 힘들어했던 이미지 업로드를 드디어 해냈다!! 야호야호~!

📂 input - file

input에는 type 중 file 이라는 속성이 존재한다.

file은 이미지, 동영상과 같은 파일을 업로드하는 기능을 가지고 있다.

export default function Image() {
  return (
    <div>
        <input type="file" />
    </div>
  );
}

위 코드를 적용시키면 아래와 같이 [파일 선택]이라는 버튼과 함께 화면에 렌더링된다.

🌠 이미지 선택 & 미리보기

이미지를 선택하고 서버에 전송하기 전, 선택한 이미지를 화면에 렌더링을 할 것이다. 그래서 handleImageChange 라는 함수를 만들고, input의 onChange에 함수를 넣어주었다.

import { useState } from 'react';

function App() {
  const [image, setImage] = useState(null);// 선택한 이미지const [imageName, setImageName] = useState(null);

  const ReadImage = (e) => {
    const file = e.target.files[0];
    const reader = new FileReader();
    reader.onloadend = () => {
      setImage(reader.result);
    };
    if (file) {
      reader.readAsDataURL(file);
      setImageName(file.name);
    }
  };
  return (
    <div className="App">
      <img src={image}></img>
      <input type="file" onChange={ReadImage}/>
    </div>
  );
}

export default App;

우선 e.target.files를 이용하여 input에서 선택한 파일을 가져온다.

그 다음 FileReader 객체를 생성하고 onloadend를 이용하여 이미지를 읽은 후, 결과값을 image state에 저장해주었다.

이미지가 잘 불러와졌다면, 그 이미지를 url로 지정해주고, 이미지의 이름을 imageName state에 저장해주었다.

🗂️ FileReader란 무엇일까?

FileReader ? 웹 애플리케이션이 비동기적으로 데이터를 읽기 위하여 읽을 파일을 가리키는 File 혹은 Blob 객체를 이용해 파일의 내용을 읽고 사용자의 컴퓨터에 저장하는 것을 가능하게 해주는 객체이다.

FileReader에는 다양한 속성, 이벤트 핸들러, 메소드가 존재하는 데 많이 쓰는 거만 적어보았다!

FileReader.result파일의 컨텐츠이다.
FileReader.onload읽기 동작이 성공적으로 완료되었을 때 발생한다
FileReader.onloadend읽기 동작이 끝났을 때마다 발생하는 것으로, 성공/실패와 상관없이 발생한다.
FileReader.readAsDataURL()컨텐츠를 특정 blob이나 file에서 읽어오는 메소드이다.base64로 인코딩된 값을 반환해준다.

기여운 미피

위 코드를 실행하면 다음과 같이 사진이 잘 렌더링된다는 것을 알 수 있다!!

올린 파일을 콘솔에 찍어보면 요런 형태로 뜬다

먼가 알아둬야 할 것 같아서.. ㅎㅅㅎ

💾 서버에 이미지 전송하기

그리고 대망의 서버에 이미지를 업로드 하는 코드를 짜볼 것이다..!

이 부분에서 제일 오류가 많이 났었다ㅜㅜ 한 6시간 동안 이 에러만 붙잡고 있었던 것 같다ㅜ

const fetchRoomInfo = async () => {
    const formData = new FormData();
    const decodedImage = await decodeImage(image);
    const imageExtension = imageName.split(".").pop();
    const blobImage = new Blob([decodedImage], {
    	type: `image/${imageExtension}`,
    });
    formData.append("roomImg", blobImage, imageName);
    formData.append("request", JSON.stringify({ roomTitle: roomName }));
    try {
      const res = await axios.post(`https://dev.writeroom.shop/rooms/createRoom`, formData, {
        headers: {
          Authorization: `Bearer ${receivedToken}`,
        },
      });
    } catch (error) {
      console.error(error);
    }
  };

  const decodeImage = async (base64Image) => {
    const blobImage = await fetch(base64Image).then((res) => res.blob());
    return blobImage;
  };

이 코드가 나의 최종 이미지 업로드 구현이다... 코드를 하나하나 뜯어보도록 하겠슴미다..

const formData = new FormData();

우선 이미지 파일은 무조건 FormData 형태로 전송해줘야 한다. 따라서 FormData 객체를 생성해주었다.

const decodeImage = async (base64Image) => {
    const blobImage = await fetch(base64Image).then((res) => res.blob());
    return blobImage;
};

그리고 이 코드는 주어진 base64 형식의 이미지를 디코딩하여 Blob 객체로 반환해주는 함수이다.

내가 이미지를 보낼 서버는 blob으로 받아야 하기 때문에 변환한 거지만

만약 file로 받는 거라면 이 과정은 생략해도 된다.

const decodedImage = await decodeImage(image);
const imageExtension = imageName.split(".").pop();
const blobImage = new Blob([decodedImage], {
    type: `image/${imageExtension}`,
});

그리고 디코딩한 이미지를 가져와준다.

imageExtension은 이미지 이름이 필요해서 가져온 것이니, 이미지의 이름이 딱히 필요없다면 이 과정도 생략 가능!!

formData.append("roomImg", blobImage, imageName);
formData.append("request", JSON.stringify({ roomTitle: roomName }));

그리고 생성해놓은 formData에 blob으로 바꿔준 이미지를 추가해준다.

아랫 줄은 서버에서 원하는 formData에 string 데이터도 있어서 넣어준 거다.

string과 blob 데이터를 한 번에 formData에 넣어서 전송한 건 처음이라 좀 많이 당황했다. 오류도 왕많이남..ㅜㅜ

그래도 어찌저찌해서 저렇게 성공을 했다 희희..

try {
  const res = await axios.post(`https://dev.writeroom.shop/rooms/createRoom`, formData, {
    headers: {
      Authorization: `Bearer ${receivedToken}`,
    },
  });
} catch (error) {
  console.error(error);
}

그리고 axios를 사용해서 formData 전송하면 끄읕~!~~!

✏️ 총정리 !!

  • input의 type 중 file 속성을 통하여 파일 선택 기능을 구현할 수 있다.
  • FileReader를 사용하여 파일을 가져오고, 그 파일을 렌더링할 수 있다.
  • 서버가 어떤 객체를 원하는 지에 따라 blob 객체로 디코딩하여 전송할 수도, 디코딩 과정 없이 그냥 전송할 수도 있다.
  • FormData에 이미지를 추가한 후, axios를 사용하여 서버에 이미지를 전송할 수 있다.

이미지 업로드에 대해서 구글링을 몇 시간 동안 했더니 이제야 이미지 파일이 어떻게 바껴서 어떻게 전송되어야 하는지에 대해 이해할 수 있었다 !!! 이 글을 보시는 분들도 이미지 업로드에 성공하시길🍀🍀