문제
기존코드는 사용자가 프로필 사진을 선택할때마다 URL로 변경해주는 /upload/image API를 타서 POST 요청을 날려주고 있다.
2022.08.20 - [React] - 로컬에 있는 이미지 파일 업로드 하기
원하는 방향
'저장'버튼을 눌렀을때만 API 요청을 보내줄 수 있도록 수정
난관
1. API를 저장할때 함수에다가 주니까 그 관련 (로컬에서 파일선택하는 코드)와의 연결성이 떨어져서 작동이 안됨
->post 요청에서 then 체이닝을 불필요하니 빼고, url값을 찾아서 return해줌 (상태 업데이트가 아니라 리턴하게 바꿔줌)
중간에 response를 꼭 콘솔 찍어보기. url을 response어디쯤에 들어가있음.
이 데이터를 정제해서 return해줘야함
이전코드
const handleImageChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const target = event.target as HTMLInputElement;
const file: File = (target.files as FileList)[0];
const imgUpload = new FormData();
imgUpload.append("image", file);
axios
.post(`${process.env.REACT_APP_BASE_URL}/upload/image`, imgUpload, {
headers: {
Authorization: `Bearer ${process.env.REACT_APP_AUTH_TEST_TOKEN}`,
},
})
.then((res) => {
console.log("res.data.data.image", res.data.data.image);
setImgUrl(res.data.data.image);
});
};
수정코드
const getImgUrl = async () => {
const imgUpload = new FormData();
imgUpload.append("image", imgFile);
const res = await axios.post(`${process.env.REACT_APP_BASE_URL}/upload/image`, imgUpload, {
headers: {
Authorization: `Bearer ${process.env.REACT_APP_AUTH_TEST_TOKEN}`,
},
});
return res.data.data.image;
};
2. axios post요청에서 formData를 넣는 부분에서 formData가 읽히지 않음
->.append쪽의 변수를 첨부하는게 아닌 new FormData()에 해당하는 변수를 넣어줌
3. 업로드 하고 URL화가 되어 있지 않으니까 브라우저에서 미리보기가 안됨.
->해결: 인코딩 하는 코드 추가하기.
const [encodedImg, setEncodedImg] = useState("");
const [imgFile, setImgFile] = useState<any>(null);
const handleImageChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const target = event.target as HTMLInputElement;
const file: File = (target.files as FileList)[0];
setImgFile(file);
//이미지 인코딩
const fileReader = new FileReader();
if (file) {
fileReader.readAsDataURL(file);
}
fileReader.onload = (event) => {
const result = event?.target?.result as string;
setEncodedImg(result);
};
};
4. 새로고침에서 get API요청을 받아 데이터가 로드될때 URL로 불러오지 않고, 이전 업로드시 인코딩 한 사진이 보임
->해결: 이에 대한 state는 따로 관리하기.
img src에서 자바스크립트 || or함수 써주기
*JSX에서 자바스립트 언어는 모두 중괄호{}를 넣어서 써줄수 있음!!
return (
<Photo>
<img src={encodedImg || profile.thumbnail} alt="thumbnail" />
<input
type="file"
style={{ display: "none" }}
accept="image/jpg,impge/png,image/jpeg"
name="profile_img"
onChange={handleImageChange}
ref={fileRef}
/>
<Camera>
<img src={camera} alt="camera" onClick={handleImageClick} />
</Camera>
</Photo>
5.비동기를 다 빼먹어 놓았다
->해결: 비동기 부분에 async await추가. axios는 네트워크 요청이기 때문에 시간이 오래걸릴수도 있는 가능성이 있으니까 axois앞에 await를 걸고 await axios.post, 그 함수는 const uploadImage = async () =>{} 해야함
*await를 안해주고 async만해서 찍으면 promise가 그대로 리턴됨
공부해야될 내용
함수
스코프
비동기 처리
then 체이닝
수정 전 코드
const [image, setImage] = useState(profile.thumbnail);
const imgUpload = new FormData();
const fileRef = useRef<HTMLInputElement>(null);
const onImageChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
const fileReader = new FileReader();
if (file) {
fileReader.readAsDataURL(file);
}
fileReader.onload = (event) => {
const result = event?.target?.result as string;
imgUpload.append(image, result);
setImage(result);
};
axios
.post(`${process.env.REACT_APP_BASE_URL}/upload/image`, imgUpload, {
headers: {
Authorization: `Bearer ${process.env.REACT_APP_AUTH_TEST_TOKEN}`,
},
})
.then((res) => {
console.log("res.data.data.image", res.data.data.image);
setImgUrl(res.data.data.image);
});
수정 후 코드
const [encodedImg, setEncodedImg] = useState("");
const [imgFile, setImgFile] = useState<any>(null);
const handleImageChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const target = event.target as HTMLInputElement;
const file: File = (target.files as FileList)[0];
setImgFile(file);
//이미지 인코딩
const fileReader = new FileReader();
if (file) {
fileReader.readAsDataURL(file);
}
fileReader.onload = (event) => {
const result = event?.target?.result as string;
setEncodedImg(result);
};
};
const getImgUrl = async () => {
const imgUpload = new FormData();
imgUpload.append("image", imgFile);
const res = await axios.post(`${process.env.REACT_APP_BASE_URL}/upload/image`, imgUpload, {
headers: {
Authorization: `Bearer ${process.env.REACT_APP_AUTH_TEST_TOKEN}`,
},
});
return res.data.data.image;
};
//저장 버튼 클릭 후 변경사항 데이터베이스 업데이트
const handleSave = async () => {
const imgUrl = await getImgUrl();
axios
.put(
`${process.env.REACT_APP_BASE_URL}/my/profile`,
{
thumbnail: imgUrl,
email: profile.email,
name: profile.name,
username: username,
countryUid: profile.countryUid,
gender: gender,
},
{
headers: {
Authorization: `Bearer ${process.env.REACT_APP_AUTH_TEST_TOKEN}`,
},
},
)
.then((res) => {});
};
배운 점
useState는 함수 맥락을 벗어나서 공중에 띄워놓고 가져다가 써야할때 사용한다. 그게 아니고 한 맥락에서 이어질때는 그냥 함수로 받아서 쓰면 된다.
한줄한줄 다 콘솔찍어보자! 어디까지 작동되고, 어느 줄 코드에서 에러가 나는지를 파악해야한다
코드의 순서는 중요하다!
'React' 카테고리의 다른 글
기업 과제 회고: 비트바이트㈜ | 플레이키보드 웹스토어 (0) | 2022.10.07 |
---|---|
프로필변경: 이미지가 바뀌지 않을시 추가 처리 (0) | 2022.08.25 |
프로필변경:put 요청을 보내고도 새로고침시 데이터가 반영이 안되는 이유 (0) | 2022.08.22 |
useState의 양방향 바인딩 (0) | 2022.08.20 |
여러 State를 관리하기 (0) | 2022.08.20 |