React

프로필변경: 이미지 업로드 할때가 아닌 저장 시에만 URL로 만들어주는 API POST 요청보내기

selonjulie 2022. 8. 24. 12:06

문제 

기존코드는 사용자가 프로필 사진을 선택할때마다 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는 함수 맥락을 벗어나서 공중에 띄워놓고 가져다가 써야할때 사용한다. 그게 아니고 한 맥락에서 이어질때는 그냥 함수로 받아서 쓰면 된다.

한줄한줄 다 콘솔찍어보자! 어디까지 작동되고, 어느 줄 코드에서 에러가 나는지를 파악해야한다

코드의 순서는 중요하다!