리액트 이미지 미리보기 구현

노성호·2021년 10월 22일
0

이미지 미리보기

기존에 구현되어 있는 이미지 미리보기가 여러 라이브러리를 쓰고 있고, 코드가 복잡해 리팩토링을 하게되었다.

input type="file" accept="image/*"

일단 input 태그를 이용해 파일을 선택한다.

<form>
  <input type="file" accept="image/*" />
</form>

// 선택한 이미지의 배열이 출력된다.
function onChange(e) {
  console.log(e.target.files);
}

img태그에 이미지를 넣으려면 src를 입력해야 한다. input 태그로 선택한 파일은 아직 업로드 되지 않았기 때문에 url을 따올수가 없었다. 그래서 검색을 좀 해보니, src에 base64 형식으로 입력하면 이미지가 생성이 된다고 했다.

const imgFile = e.target.files[0];
const reader = new FileReader();
reader.onloadend = () => {
  const base64Src = reader.result;
  handleImagePreview(base64Src);
}
reader.readAsDataUrl(imgFile);

FileReader를 이용하면 이미지 파일의 base64 문자열을 얻을 수 있었다. 이렇게 얻어낸 base64 문자열을 img 태그의 src에 넣어주면 끝.

FileReader가 조금 헷갈렸던게, reader.onloadendreader.readAsDataUrl의 순서가 반대인듯 해서 시간을 좀 썼었다. 알고보니 onloadend는 FileReader가 파일을 모두 읽고나서 실행되는 이벤트를 할당하는 것이고, reader.readAsDataUrl은 FileReader로 읽을 파일을 넣어주는 것이었다.

리액트에서

const inputRef = useRef<HTMLInputElement | null>(null);

function handleChangeForm(propertyName: keyof FormData, value: unknown) {
  // props로 내려받은 핸들러가 상태를 변경한다.
  props.handler({...props.formData, [propertyName]: value});
}

function onChange(e) {
  e.preventDefault();
  if(!e.target.files) return;
  const imgFile = e.target.files[0];
  const reader = new FileReader();
  reader.onloadend = () => {
  	const imgBase64 = typeof reader.result === 'string' ? reader.result : '';
    // 이미지 src를 변경하여 이미지 미리보기를 해준다.
    handleChangeForm("image", imgBase64);
  }
  reader.readAsChangeForm(imgFile);
}

return (
  <form>
    <input type="file" accept="image/*" /> // hidden 처리
  </form>
  // html 인풋은 숨기고, React의 Button 컴포넌트를 만들어서 사용한다.
  <Button 
    onClick={() => {
      if(!inputRef) return;
      inputRef.current.click(); // 숨겨놓은 인풋의 클릭 이벤트를 발생시킨다.
    }}
  >
    이미지 업로드
  </Button>
)

Blob을 이용하면 파일이 생성된 window의 document(브라우저)에서만 유효한 url이 생성된다고 한다. 다른 window에서 재활용 할 수 없으며 url의 수명이 한정되어 있기 때문에 file:URL과 달리 보안 이슈에서 벗어날 수 있다고 한다.

0개의 댓글