[React] 이미지 업로드 & 미리보기

이지·2023년 8월 21일
0

상품 등록을 위해 상품 이미지를 업로드 해야하는 경우 어떻게 업로드하고 미리볼 수 있는지 알아보고자 합니다.

전체 코드

// 전체 코드 중에서 이미지 업로드에 관련된 코드만 가져왔습니다.
export default function ProductAdd() {
  const [product, setProduct] = useState({
	...
    image: "",
    ...
  });
  const inputImgRef = useRef(null);
  
  const handleImgChange = () => {
      const file = inputImgRef.current.files[0]; 
      if (!file) {
        return;
      }
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onloadend = () => {
        setProduct({ ...product, image: reader.result });
      };
    };
  
   const onClickInput = () => {
     inputImgRef.current.click();
   };

  return (
    <ProductImg>
        <label htmlFor="image">상품 이미지</label>
        <ProductImgInputContainer>
        <input
            type="file"
            id="image"
            name="image"
            accept=".jpg,.jpeg,.png,.gif"
            ref={inputImgRef}
            onChange={handleImgChange}
        />
		<ProductImgInputBtn type="button" onClick={onClickInput}>
        	<img src={imgUploadBtn} alt="이미지 추가하기" />
    	</ProductImgInputBtn>
        {product.image && (
            <ProductImgPreview
                src={product.image}
                alt="상품 미리보기"
                onClick={onClickInput}
            />
        )}
        </ProductImgInputContainer>
    </ProductImg>
  );
}

이미지 업로드

  • input type="file"을 사용하게 되면 위의 이미지처럼 파일 선택 버튼이 예쁘지 않다.
    <ProductImgInputBtn type="button" onClick={onClickInput}>
        <img src={imgUploadBtn} alt="이미지 추가하기" />
    </ProductImgInputBtn>
그래서 위의 코드를 추가해서 원하는 이미지 파일을 이용해 이미지를 추가할 수 있는 버튼을 만들었습니다.
(+)input에 display: none을 추가하여 기존의 파일 선택 버튼이 보이지 않도록 해줌.

  • input의 accept 속성 을 통해 파일의 종류를 제한할 수 있습니다.

    • 파일확장자 : .jpg, .jpeg, .png, .gif
    • MIME 타입 : image/jpg, image/jpeg, image/png와 같이 파일의 종류와 서브타입을 지정할 수 있습니다.
  • onClickInput
    ProductImgPreview과 ProductImgInputBtn의 onClick 이벤트로 onClickInput 함수가 실행되는데, 이 함수는 inputImgRef를 통해 파일 입력 요소에 대한 참조를 얻어옵니다. 이미지 업로드 버튼이나 사용자가 이미지 미리보기 영역(이미지가 업로드되어 미리보기가 된 경우에도 이미지를 수정할 수 있도록 추가해주었음)을 클릭할 경우, inputImgRef.current.click()을 호출하여 파일 입력 요소의 클릭 이벤트를 강제로 발생시키는 역할을 합니다.

  • handleImgChange

    • 이 과정을 통해 사용자가 이미지 파일을 선택하면 해당 이미지 파일이 읽어져 들어오고, 그 결과를 바탕으로 이미지 미리보기가 표시됩니다. 이를 통해 사용자는 자신이 선택한 이미지가 어떻게 보이는지 확인할 수 있습니다.
  const handleImgChange = () => {
        const file = inputImgRef.current.files[0]; // 파일 입력 요소에서 사용자가 선택한 파일 객체를 가져옵니다.
        
        if (!file) { // 파일 객체가 없는 경우 함수 종료(예: 사용자가 취소 버튼을 누른 경우)
          return;
        }
        const reader = new FileReader(); // 파일 객체가 존재하면, FileReader 객체를 생성하여 선택한 이미지 파일을 읽습니다.
        reader.readAsDataURL(file); // 파일을 데이터 URL 형식으로 읽어옵니다. 이렇게 하면 이미지 파일을 Base64 형식의 문자열로 인코딩할 수 있습니다.
        reader.onloadend = () => {
          setProduct({ ...product, image: reader.result }); // setProduct 함수를 호출하여 이미지 프리뷰에 표시할 이미지 데이터를 업데이트해주는 작업을 수행합니다.
        };
      };
  • if(!file) { return; }
    • 해당 조건을 추가해준 이유 : 이미 파일 하나가 선택되어 미리보기가 되고 있는 상황에서 이미지를 수정하고자 한번 더 눌러 파일을 선택하려 했다가 취소하게 되면 아래와 같은 에러가 발생하게 됩니다.
    • 여기서의 문제는 취소 버튼을 누른 경우에도 이벤트가 발생하면서 file 객체가 없음에도 함수가 실행되는 것이고, 이로 인해 FileReader에 올바른 Blob 객체가 아닌 null 혹은 undefined 값이 전달되어 에러가 발생합니다. 이 문제를 해결하기 위해 handleImgChange 함수에서 file 객체의 존재 여부를 체크하고, file 객체가 없는 경우 함수를 종료하도록 수정하면 됩니다.

이미지 미리보기

    {product.image && (
                <ProductImgPreview
                    src={product.image}
                    alt="상품 미리보기"
                    onClick={onClickInput}
                />
            )}
  • {product.image && (...)} 는 product 객체가 이미지 데이터를 가지고 있을 경우에만 미리보기 이미지가 나타나도록 하는 조건문입니다.
  • handleImgChange 함수가 실행됨으로써 setProduct에 선택된 이미지가 저장되고 이를 통해 미리보기가 가능해집니다.

0개의 댓글