서버와 url을 주고받는 기능을 구현하기 이전에
사용자 입력으로 이미지를 업로드하고, 업로드한 이미지를 클라이언트측에서 미리보기로 확인할 수 있는 기능을 구현하고자 했다.
FileReader와 createOjbectURL 중 createObject URL방식이 변환된 string의 길이가 더 짧고, 인코딩 디코딩 과정을 거치지 않아도 된다는 점에서 해당 방식을 활용하기로 결정했다.
참조 링크
https://velog.io/@yeogenius/React-클라우드-스토리지에-이미지-파일-업로드-하기
https://velog.io/@xxyeon129/Formdata로-이미지-업로드하기
https://hojung-testbench.tistory.com/entry/React-파일-업로드-기능-구현
https://mieumje.tistory.com/164
웹에서 멀티미디어 데이터를 다룰 때 Blob이라는 데이터 타입을 주로 사용한다.
파일이나 영상 이미지와 같이 용량은 큰 파일은 key,valuye 쌍으로 이루어진 json 형식으로 다루기 어렵기 때문이다.
Blob (Binary Large Object)에는 데이터 사이즈를 알아내거나, 데이터 송수신을 위해서 더 작은 blob객체로 나누는 등, 다양한 범위에서 사용될 수 있다.
Blob은 데이터 자체라기보다, 데이터를 처리하거나 간접적으로 접근하기 위한 객체라고 볼 수 있다.
Blob을 상속받은 File 객체로 로컬 파일을 다룰 수 있으며, 추가로 파일명과 최종 수정일을 확인할 수 있는 프로퍼티가 추가되어있다.
해당 메소드를 통해 사용자가 업로드한 파일 미리보기 기능을 구현할 수 있다.
createObjectURL()
메서드는 File, Blob 객체를 가르키는 URL을 생성한다.revokeObjectURL()
을 통해서 그 전에 해제할 수 있다.이미지를 사용자로부터 받기 위해서는 input 태그를 활용해야 한다.
하지만 버튼기능을 통해서 사용자로부터 이미지를 받기 위해서 input 태그는 hidden 속성을 적용하고, useRef를 활용하여 input태그에 직접적으로 접근하여 값을 가져오도록 했다.
const fileInputRef = useRef(null); // file input ref
// input을 가르키는 ref
const handleFileInput = useCallback(() => {
if (!fileInputRef.current) {
return;
}
fileInputRef.current.click();
}, []);
<button onClick={handleFileInput}>
Upload
</button>
<input
className="hidden"
type="file"
accept="image/jpg, image/jpeg, image/png"
ref={fileInputRef}
onChange={onImageChange}
/>
input 태그를 통해 업로드된 파일이 바뀔 때 마다 해당 메소드가 실행된다.
해당 메소드를 통해서 입력된 파일의 정보를 저장하고, URL을 생성하여 미리보기 기능을 구현할 수 있다.
const [imageFile, setImageFile] = useState(null); // upload한 파일 정보 저장
// 입력한 이미지 데이터 처리
const onImageChange = useCallback((e) => {
const fileList = e.target.files; // 입력된 파일 형태의 데이터
// 하나의 파일 데이터만 처리
if (fileList && fileList[0]) {
setImageFile(fileList[0]); // 업로드 된 파일의 정보 저장
}
}, []);
하나의 파일만 다루기때문에 fileList 배열에 [0] 번째 해당하는 인덱스의 값만 활용한다.
파일이 업로드 되면 createObjectURL 메소드를 통해서 fileList[0] 객체의 URL을 생성한다.
const [imagePreview, setImagePreview] = useState(""); // upload한 파일 미리보기
// 입력한 이미지 데이터 처리
const onImageChange = useCallback((e) => {
const fileList = e.target.files; // 입력된 파일 형태의 데이터
// 하나의 파일 데이터만 처리
if (fileList && fileList[0]) {
setImageFile(fileList[0]); // 업로드 된 파일의 정보 저장
// 이미지 미리보기 기능을 위해서 브라우저상에서 url을 생성함
const imagePreviewUrl = URL.createObjectURL(fileList[0]);
setImagePreview(imagePreviewUrl);
}
}, []);
createObjectURL로 생성된 Blob URL은 revoekObjectURL을 호출하기 전까지 브라우저 메모리에 존재한다.
따라서 메모리 누수를 방지하기 위해서, imagePreview의 값이 변경될 때 마다 revokeObjectURL을 호출하여 Blob URL을 해제한다.
// 이미지 업로드 후 Blob URL 해제(메모리 누수 방지용)
useEffect(() => {
if (imagePreview) {
return () => URL.revokeObjectURL(imagePreview);
}
}, [imagePreview]);