글 업로드 할 때 이미지 프리뷰를 보여주기위해서는
URL.createObjectURLvsFileReader.readAsDataURL두 가지 방법을 활용할 수 있다. 이걸 비교해 보고자 한다

URL.createObjectURL: 브라우저 메모리 내 객체를 참조하는 임시 blob URL 생성FileReader.readAsDataURL: 파일 내용을 base64 문자열로 변환해 "data:..." 형식으로 생성URL.createObjectURL: 동기적으로 실행(즉시 실행)FileReader.readAsDataURL은 비동기적으로 실행(시간이 조금 지체된 후 실행)URL.createObjectURL : 직접 revokeObjectURL로 해제 필요(또는 문서 종료 시 해제)FileReader.readAsDataURL : 결과가 긴 문자열이라(base64) Blob URL(createObjectURL)에 비해 메모리를 많이 잡아먹지만, 사용하지 않으면 자동으로 가비지 컬렉터에 의해 자동 제거URL.createObjectURL, URL.revokeObjectURLimport { useEffect, useRef, useState } from "react";
export default function ImagePreview() {
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
const handleChangeImage = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
// 새 URL 만들기 전에 이전 URL 해제
setPreviewUrl((prev) => {
if (prev) URL.revokeObjectURL(prev);
return URL.createObjectURL(file);
});
};
// 컴포넌트 언마운트 시 마지막 URL 해제
useEffect(() => {
return () => {
if (previewUrl) URL.revokeObjectURL(previewUrl);
};
}, [previewUrl]);
return (
<>
<input type="file" accept="image/*" onChange={handleChangeImage} />
{previewUrl && (
<img
src={previewUrl}
alt="preview"
// 이미 로딩이 끝났다면 바로 해제하는 패턴도 가능(선택)
onLoad={() => {
// URL.revokeObjectURL(previewUrl); // 선택사항
}}
/>
)}
</>
);
}
FileReader.readAsDataURLimport { useState } from "react";
export default function ImagePreviewBase64() {
const [previewDataUrl, setPreviewDataUrl] = useState<string | null>(null);
const handleChangeImage = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
const reader = new FileReader(); //파일 리더 객체 지정
reader.readAsDataURL(file);//파일 객체를 읽어서 이해 가능한 문자열로 변경
reader.onload = () => {
// onload 시점에만 result가 채워짐(비동기)
setPreviewDataUrl(reader.result as string);
};
};
return (
<>
<input type="file" accept="image/*" onChange={handleChangeImage} />
{previewDataUrl && <img src={previewDataUrl} alt="preview" />}
</>
);
}