기존에 구현되어 있는 이미지 미리보기가 여러 라이브러리를 쓰고 있고, 코드가 복잡해 리팩토링을 하게되었다.
일단 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.onloadend
와 reader.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과 달리 보안 이슈에서 벗어날 수 있다고 한다.