const PostItem = ({ navigation, route }) => {
{...}
// 판매글 업로드 시 필요한 이미지 state
const [image, setImage] = useState([]);
// 판매글 업로드 시 필요한 body에 담길 내용
const [body, setBody] = useState({
order_status: "판매중",
category: "",
title: "",
price: "",
description: "",
access_range: 1,
});
const updateImage = (image) => {
setImage(image);
};
// 기능 별로 나눈 Component에 setBody를 넘겨주기 위한 함수
const updateData = (data) => {
setBody({ ...body, [data.type]: data.value });
};
// 이미지 전송을 위해 FormData 작성 함수
const createFormData = (images, body) => {
const data = new FormData();
images.forEach((el) => {
data.append("product_image", {
uri: el.uri.replace("file://", ""),
type: `${el.type}/${el.uri.split(".").pop()}`,
});
});
Object.keys(body).forEach((key) => data.append(key, body[key]));
return data;
};
{...}
};
export default PostItem;
PostItem은 판매글을 업로드 하는 페이지이다.
여기서 어려웠던 것은 이미지 파일(URL이 아님..)을 백엔드로 보내는 것 이었는데,
그 동안은 이미지를 주고 받을 때, url을 주고 받으며 통신 했지만 이번엔 실제 이미지
파일을 전송해야 해서 FormData라는 새로운 타입을 공부해야 했다.
Request를 어떤식으로 작성해서 보내줘야 하는지 몰라서 초반에 고민을 많이 했었다.
수 많은 검색을 통해 form-data를 작성하는 방법은 알게 되었지만,
결국 멘토님의 리뷰를 통해 가장 좋은 형태를 띈 form-data가 완성되었다!!
createFormData()
를 통해 생성된 data
를 출력해보니
아래와 같은 형태(터미널에선 좀더 지저분한 형태로 보여서 보기 좋게 수정...)로 출력.
{
"_parts": [
[
"product_image",
{
"type": "image/png",
"name": "파일명.png",
"uri": "파일URI.png"
}
],
[
"product_image",
{
"type": "image/jpg",
"name": "파일명.png",
"uri": "파일URI.jpg"
}
],
["order_status", "판매중"],
["category", "게임/취미"],
["title", "지갑 팝니다."],
["price", "99999999"],
["description", "사고 3번정도 밖에 들고 다니지 않았던 최상품 입니다."],
["access_range", 1]
]
}
const createFormData = (images, body, category) => {
const data = new FormData();
images.forEach((el) => {
data.append("image", {
type: `${el.type}/${el.uri.split(".").pop()}`,
name: el.uri.split("/").pop(),
uri: el.uri.replace("file://", ""),
});
});
Object.keys(body).forEach((key) => data.append(key, body[key]));
data.append("product_category", category);
return data;
};
백엔드에서 이미지를 받으려면 file, image 두가지 형태로 보낼수 있는것 같다.
data.append("image",{})
꼭 이렇게 해야만 백엔드에서 읽을 수 있는건지 모르겠는데. (이 부분은 좀더 공부를 해봐야 할것 같다.)
그래서 아래와 같이 form-data를 작성한다. (type, name, uri 3가지를 보내줘야 한다.)
꼭 3가지를 다 보내야 하는지는 모르겠는데, 일단 이번 프로젝트에서는 3가지 모두 보내줘야지 백엔드에서 읽을수 있었다.
data.append("image", {
type: `${el.type}/${el.uri.split(".").pop()}`,
name: el.uri.split("/").pop(),
uri: el.uri.replace("file://", ""),
});
fetch(`${ITEM_LIST_API}?address_id=${addressId}`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "multipart/form-data;",
Authorization: AsyncStorage.getItem("token"),
},
body: createFormData(image, body, postCategory),
})
.then((response) => response.json())
.then((result) => {
if (result.message === "SUCCESS") dispatch(initCategory());
else Alert.alert("알림", "글쓰기 실패", [{ text: "닫기" }]);
});
expo-image-picker
라이브러리를 이용하면
앨범에서 이미지를 가져오면 uri가 이런 이상한 모양으로 나온다.ㅋㅋㅋㅋ
그리고 type은 image 밖에 안온다....ㅋㅋㅋㅋㅋ
"file:///var/mobile/Containers/Data/Application/738480BE-5686-477A-BAC7-F8FBD9273708/Library/Caches/ExponentExperienceData/%2540kingth_man%252FKiwiMarket/ImagePicker/2EE95964-0CFD-4A8C-9D74-1BCED984DA05.jpg"
form-data에서 사용할 형태로 파싱을 해보자!
ios에서는 "file://" 요런 녀석이 앞에 붙어서 떼어내준다.
type은 "image/jpg" 모양으로 만들어 줘야 했다.ㅋㅋㅋ
type: `${el.type}/${el.uri.split(".").pop()}`,
name: el.uri.split("/").pop(),
uri: el.uri.replace("file://", ""),
Object {
"type": "image/jpg",
"uri": "/var/mobile/Containers/Data/Application/738480BE-5686-477A-BAC7-F8FBD9273708/Library/Caches/ExponentExperienceData/%2540kingth_man%252FKiwiMarket/ImagePicker/A2AE17D5-AE65-49CC-9B44-12626B0C013C.jpg",
},
원하는 형태로 잘 만들어 졌다.
Object.keys()
뭐하는 녀석?여기서 재밌었던 부분은 기존의 fetch방식에서 사용했던 body를 생각해서
이름 지은 state(body) 객체를 해체하고 를 각각의 키마다 [ key, value ] 배열을
하나씩 FormData.append()
해줘야 했다.
Object.keys()
어디선가 본 기억은 있는데...
그 당시엔 이걸 어디써? 라고 생각했는데..ㅋㅋㅋ
Object.keys(body)
는 body의 키들로 이루어진 배열을 반환한다.
[
'title',
'price',
'description',
'access_range'
]
이렇게 javascript 문법이 중요하단걸 또 한번 느끼게 되었다!!