Trouble Shooting

[yesorno 프젝]input에 같은 파일 재업로드하기(by onChange)

봄나물소년 2023. 12. 20. 18:34

File Upload

js에서 파일을 업로드를 하려면 type="file"을 속성으로하는 input을 만들어 사용한다.

그리고 해당 input에 onChange를 달아 파일을 업로드하면 onChange event가 발생하여 파일이 업로드가 된다.

이번 1차 qa 후 이 파일업로드 쪽에서 이슈가 하나 발생했다.

바로 이미지 업로드 -> 삭제 -> 이미지 업로드 시 이미지가 아무것도 업로드되지 않는 이슈였다.

여기서 중요한 점은 "같은" 파일을 재업로드했을 때만 해당 이슈가 발생한 것 이다.

다른 파일을 업로드하면 문제가 없다.

 

onChange안에서 콘솔을 찍어보니 

아무것도 뜨지않는다.

mdn에서 onChange에 대하여 찾아보았다.

"값을 변경할 떄" 발생한다고 한다.

내가 같은 파일을 올리니 값이 실질적인 value의 변화가 일어나지 않아 onChange가 trigger되지 않았던 것!

 

React에서 구현 

export default function ImageUploader({handleImage}: ImageUploaderProps) {
  const fileInput = useRef<HTMLInputElement>(null);
  const setSelectedImgIndex = useSetRecoilState(selectedImgIndexState);
  return (
    <>
      <ImageUploadBtn htmlFor="input-file">
        <AddIcon/>
        <p>사진 추가</p>
      </ImageUploadBtn>
      <input 
        type="file" 
        id="input-file" 
        name='image_URL' 
        accept='image/*' 
        ref={fileInput} 
        onChange={(e: React.FormEvent<HTMLInputElement>) => {
          handleImage(e);
          setSelectedImgIndex(null);
        }} 
        style={{display: "none"}} />
    </>
  )
};
  • input의 onChange부분을 보면 다음과 같은 함수들이 실행 중이다
    handleImage(e): 프리뷰 이미지 생성과 게시물 생성하기 위해 이미지를 상태값에 담아주는 함수
    setSelectedImgIndex(null): 직전에 선택된 기본 이미지의 버튼 활성화 상태를 초기화(style을 없애버림) 
  const handleImage = async (e: any) => {
    const file = e.target.files;
    if(!file) {return};

  const imageFile = file[0];
  const options = {
    maxSizeMB: 2.0,
    maxWidthOrHeight: 500,
  };
  
  try{
  	// 이미지 최적화
    const compressedFile = await imageCompression(imageFile, options);
    const convert = new File([compressedFile], imageFile.name, {
      type: `${imageFile.type}`
    });
    // 최적화된 이미지를 file객체로 다시 가져와 storage 업로드를 위한 상태값에 할당
    setFile(convert);
    // 파일 객체를 리더기에 넣어 url로 만들어준 뒤 프리뷰를 위한 상태값에 할당 
    const reader = new FileReader();
    reader.readAsDataURL(convert);
    reader.onloadend = () => {
      setImageUrl(reader.result);
      e.target.value = ''; // value 값 초기화
    }
  }catch(error) {
    console.log(error);
  }
};
  • file 객체는 reader.readAsDataURL을 통해 읽는다. MDN에 따르면
더보기

readAsDataURL 메서드는 컨텐츠를 특정 Blob 이나 File에서 읽어 오는 역할을 합니다. 읽어오는 read 행위가 종료되는 경우에, readyState (en-US) 의 상태가 DONE이 되며, loadend (en-US) 이벤트가 트리거 됩니다. 이와 함께, base64 인코딩 된 스트링 데이터가 result 속성(attribute)에 담아지게 됩니다.

데이터를 url로 바꿔주는 작업이 끝난 뒤(reader.onloadend) 이전에 들어온 value 값을 초기화해주니 같은 이미지 재업로드가 된다.