이미지의 미리보기와 수정에 FormData
를 사용하는 이유와 관련된 전체적인 흐름과 로직에 대해 알아보자!!
사용자가 이미지 선택: 웹 페이지에 <input type="file">
요소를 사용하여 사용자로부터 이미지나 파일을 선택받는다.
미리보기 생성: 선택된 이미지를 브라우저에서 바로 보여주기 위해 FileReader API를 사용하여 이미지를 읽어 들인다. 읽어들인 이미지 데이터는 Blob 또는 Data URL 형식으로 생성되며, 이를 <img>
태그의 src
속성에 할당하여 미리보기를 제공한다.
이미지 수정 및 업로드 준비: 사용자가 이미지를 수정하거나 다른 이미지를 선택하면, 해당 이미지 데이터는 FormData 객체에 포함되어 서버로 전송될 준비를 한다.
서버로 이미지 전송: 수정된 이미지를 서버에 저장하기 위해 FormData 객체를 사용하여 서버에 POST 또는 PUT 요청을 한다.
멀티파트 폼 데이터 전송: 이미지와 같은 바이너리 파일은 HTTP 요청 본문에 직접 포함시키기 어렵다. 이러한 경우, 멀티파트 형식(multipart/form-data
)을 사용하여 파일과 텍스트 데이터를 함께 전송할 수 있다. FormData API는 이 멀티파트 형식을 자동으로 관리해주므로, 파일과 텍스트 데이터를 함께 쉽게 전송할 수 있다.
동적 폼 데이터 생성: JavaScript를 사용하여 동적으로 폼 데이터를 생성하거나 수정할 수 있다. FormData 객체는 키-값 쌍 형태로 데이터를 추가, 삭제, 수정하는 메서드를 제공한다.
브라우저 호환성: 대부분의 모던 브라우저에서 FormData API를 지원한다.
File Input 변경 감지: 사용자가 파일 입력 요소를 통해 이미지를 선택하면 onChange
이벤트가 발생한다.
이미지 미리보기: 선택된 이미지는 FileReader API를 사용하여 읽히고, 미리보기 이미지로 표시된다.
FormData 생성: 이미지 미리보기 후, 해당 이미지 데이터는 FormData 객체에 추가된다. 이 객체는 나중에 서버로 이미지를 전송하기 위한 준비로 사용된다.
이미지 전송: 사용자가 "저장" 버튼을 클릭하면, FormData 객체가 서버로 전송된다. 여기서 Content-Type: multipart/form-data
헤더를 통해 이미지와 함께 다른 폼 데이터도 함께 전송될 수 있다.
이러한 흐름을 따르면, 웹 애플리케이션은 사용자에게 이미지 미리보기 기능을 제공하면서도, 실제 이미지 데이터는 서버로 안전하게 전송할 수 있다.
이 함수는 선택된 파일(이 경우 이미지)에 대한 Blob URL을 생성한다. Blob URL은 파일의 내용을 직접 참조하는 URL이며, 해당 URL을 <img>
태그의 src 속성에 설정하면 브라우저가 이미지를 렌더링한다.
장점: 빠르고 효율적이다. 큰 파일에 대해서도 상대적으로 빠르게 작동한다.
단점: Blob URL은 메모리를 사용하므로 더 이상 필요하지 않을 때 window.URL.revokeObjectURL(url)을 호출하여 해제하는 것이 좋다.
if (file) {
const image = window.URL.createObjectURL(file);
setImgFile(image);
// 새로운 FormData 생성
const newFormData = new FormData();
newFormData.append('image', file);
// FormData를 상태로 설정
setFormData(newFormData);
}
FileReader 객체를 사용하여 선택된 파일을 Data URL로 읽는다. Data URL은 파일의 내용을 Base64로 인코딩한 문자열 형태의 URL이다. 이 URL을 <img>
태그의 src 속성에 설정하면 브라우저가 이미지를 렌더링한다.
장점: Base64로 인코딩된 데이터를 포함하므로 별도의 해제 과정이 필요하지 않다.
단점: Base64 인코딩은 원본 파일 크기보다 약 33% 더 크기 때문에 큰 이미지의 경우 메모리 사용량이 더 클 수 있다. 또한, 인코딩 과정에서 약간의 시간이 걸릴 수 있다.
if (file) {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
setImgFile(reader.result as string);
// 새로운 FormData 생성
const newFormData = new FormData();
newFormData.append('image', file);
// FormData를 상태로 설정
setFormData(newFormData);
};
}
이미지를 수정하기 위해 axios
를 사용하여 PUT
요청을 보내려면 FormData
객체를 사용해야 한다. FormData
객체는 multipart/form-data
인코딩을 사용하여 요청 본문에 파일 데이터를 포함시킬 수 있다.
다음은 axios
를 사용하여 이미지를 수정하기 위한 PUT
요청을 하는 방법에 대한 기본적인 단계이다.
FormData 객체 생성:
const formData = new FormData();
이미지 파일 추가: 파일 입력 필드나 드래그 앤 드롭 방식 등을 통해 사용자가 선택한 이미지 파일을 FormData
객체에 추가한다.
formData.append('profileImage', selectedFile); // 'profileImage'는 서버에서 예상하는 필드 이름입니다.
axios를 사용하여 PUT 요청 보내기:
import axios from 'axios';
axios.put('https://your-api-endpoint.com/profile', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(response => {
console.log('Image successfully updated', response.data);
})
.catch(error => {
console.error('Error uploading image', error);
});
multipart/form-data는 주로 HTML 폼에서 파일 업로드를 할 때 사용하는 MIME 타입이다. 여러 파트로 나누어진 데이터를 하나의 요청에 포함시키기 위해 사용되며, 각 파트는 자체적인 헤더 세트를 가진다.
왜 사용하는가?
파일 업로드: 웹 어플리케이션에서 파일을 업로드 할 때, 파일의 바이너리 데이터와 폼의 다른 필드(텍스트 등)를 함께 전송할 수 있다.
복합 데이터 전송: 하나의 요청에서 텍스트와 파일, 그 외의 데이터를 함께 전송할 수 있다.
웹에서는 특히 이미지나 동영상 등의 미디어 파일, 그외의 바이너리 데이터를 전송할 때 이 방식이 사용된다.
axios나 다른 HTTP 클라이언트 라이브러리를 사용하여 프로그래밍 방식으로 요청을 보낼 때, 해당 요청이 파일을 포함한다면 Content-Type 헤더를 multipart/form-data로 설정해야 한다.
const saveImgFile = () => {
if (fileInputRef.current) {
const file = fileInputRef.current.files?.[0];
if (file) {
if (file.size > 5 * 1024 * 1024) {
toast.error('5MB 이하만 업로드 가능해요 🙇♀️');
return;
}
if (
!['image/jpeg', 'image/jpg', 'image/png', 'image/bmp'].includes(
file.type,
)
) {
toast.error('지원하지 않는 파일 형식입니다!');
return;
}
}
if (file) {
const image = window.URL.createObjectURL(file);
setImgFile(image);
// 새로운 FormData 생성
const newFormData = new FormData();
newFormData.append('newImage', file);
// FormData를 상태로 설정
setFormData(newFormData);
}
}
console.log(formData);
};
const putProfile = async () => {
try {
const response = await axios.put(`${API_BASE_URL}/api/member/update/profile`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `${localStorage.getItem('Authorization')}`
},
});
console.log(response);
} catch (error) {
console.log(error);
}
}