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

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를 사용하여 서버에 이미지를 전송할 수 있다.
이미지 업로드에 대해서 구글링을 몇 시간 동안 했더니 이제야 이미지 파일이 어떻게 바껴서 어떻게 전송되어야 하는지에 대해 이해할 수 있었다 !!! 이 글을 보시는 분들도 이미지 업로드에 성공하시길🍀🍀
'React' 카테고리의 다른 글
[React] React Hooks (0) | 2024.09.18 |
---|---|
[React] Vite 사용하기 (1) | 2024.09.07 |
[React] Swiper.js 사용하여 슬라이더 구현하기 (0) | 2024.09.01 |
[React] 디자인 패턴 : MVC, MVVM, MVP, Flux (0) | 2024.08.22 |
[React] SPA와 MPA, CSR과 SSR (0) | 2024.08.20 |