프로젝트에서 프로필 사진을 변경할 때, 이미지를 미리보기로 띄울 수 있도록 구현하고자 했다.
미리보기로 띄울 수 있는 방식으로 FileReader와 createObjectURL 방식 두 가지를 알게되었다.
FileReader를 흔히 들어서 처음엔 당연하게 FileReader를 사용해서 구현했지만,
두 방식을 비교하는 글을 보게되었고 실제로 테스트도 해보면서 두 방식 간에 차이가 있어서 남겨보고자 한다.
FileReader
파일 이미지를 업로드 한 후, 미리보기로 보여줄 수 있는 FileReader 객체가 있다.
FileReader 객체는 file이나 blob 객체가 저장하고 있는 데이터를 웹 애플리케이션에서 '비동기적'으로 읽는 객체로, 파일 내용을 읽고 사용자의 컴퓨터에 저장하도록 해준다.
FileReader 순서
1) read
2) load event
3) result
- FileReader 생성
const reader = new FileReader();
- File Load 이벤트
reader.onload = () => {
setImage(reader.result)
}
load 이벤트의 핸들러로, 읽기 동작이 완료 되었을 때마다 발생한다.
FileReader.result는 파일의 컨텐츠이다.
- FileReader URL 메서드
if (uploadFile) {
reader.readAsDataURL(uploadFile)
} else {
return
}
파일 컨텐츠를 읽고 완료되면 결과 속성에 파일의 데이터를 나타내는 URL이 포함된다.
이 외에도 에러를 나타내는 FileReader.error 속성,
에러 핸들러인 FileReader.onerror,
읽기 요청을 중단시키는 FileReader.abort() 메서드 등이 존재한다.
공식문서: https://developer.mozilla.org/ko/docs/Web/API/FileReader
- 함수 실행
<input type="file" id="fileInput"
accept='image/jpg, image/png, image/jpeg, image/gif'
style={{ display: 'none' }}
onChange={onChange}
ref={fileInput}/>
파일을 첨부하면, 앞에서 선언한 onChange 함수가 실행된다.
- 전체 코드
// ...
const onChange = (e) => {
const uploadFile = e.target.files[0]
const reader = new FileReader();
reader.onload = () => {
setImage(reader.result)
}
if (uploadFile) {
reader.readAsDataURL(uploadFile)
} else {
return
}
}
return (
// ...
<input type="file" id="fileInput"
accept='image/jpg, image/png, image/jpeg, image/gif'
style={{ display: 'none' }}
onChange={onChange}
ref={fileInput}/>
// ...
)
createObjectURL
다음으로는 createObjectURL 방식이 있다.
createObjectURL는 URL 객체의 메서드로, File이나 Blob 객체를 가리키는 고유한 URL을 생성한다.
고유한 URL 객체는 Document가 닫히기 전까지 유지된다.
- URL revoke
if (image) URL.revokeObjectURL(image);
- URL 생성
const imgUrl = URL.createObjectURL(uploadImg);
- 전체 코드
// ...
const onImageChange = (e) => {
handleInputChange(e)
const uploadImg = e.target.files[0]
if (image) URL.revokeObjectURL(image);
const imgUrl = URL.createObjectURL(uploadImg);
setImage(imgUrl)
};
return (
// ...
<input type="file" id="fileInput"
accept='image/jpg, image/png, image/jpeg, image/gif'
style={{ display: 'none' }}
onChange={onImageChange}
ref={fileInput}/>
// ...
)
위처럼 고유한 URL을 생성해서 브라우저 메모리에서 가지고 있는다.
FileReader vs createObjectURL
두 방식이 겉으로 보기에는 굉장히 유사하고 사실상 큰 차이는 없어보인다.
그러나 처음에 FileReader를 사용하면서 FileReader로 생성되는 URL을 보고는 깜짝 놀랐다.
길이가 굉장히 길었고 알아보기조차도 힘들었다.
두 방식은 메모리, 속도, 수용 가능 용량에 차이가 있다고 한다.
- 메모리
FileReader 방식은 base24 인코딩 문자열을 사용하며 사용이되지 않을 때는 garbage collector에 의해서 자동으로 수거가 된다.
createObjectURL 방식은 포인터를 사용하고 Document가 닫히거나 revoke되지 않으면 메모리에 계속 남아있는다.
그러나, FileReader의 string이 훨씬 길기 때문에 createObjectURL 방식에서 화면 이동 간에 자동으로 revoke되고 필요할 때마다 revoke를 해주면 되기 때문에 메모리 측면에서 더 효율적이다.
- 속도
FileReader 방식은 File or Blob 객체를 읽고 base24 인코딩 문자열로 변환하는데 여러 작업을 거치며 비동기적으로 처리가 되어야 한다.
createObjectURL 방식은 동기적으로 동작하고 File or Blob 객체를 읽지 않고 URL을 즉시 생성한다.
- 수용 가능 용량
FileReader 방식은 약 10MB의 용량을 수용할 수 있으며
createObjectURL 방식은 800MB 정도의 용량까지 수용할 수 있다고 한다.
참고자료
https://mieumje.tistory.com/164
https://velog.io/@rlawodh123/React-%ED%8A%B8%EC%9C%84%ED%84%B0-%ED%81%B4%EB%A1%A0-Firebase-Profile
'IT Study > React' 카테고리의 다른 글
[Front/React] react-error-boundary 라이브러리로 에러 처리하기 (0) | 2023.12.09 |
---|---|
[Front/React] useMemo()로 효율적으로 관리하기 (1) | 2023.12.08 |
[Front/React] 프론트엔드 쿠키 & JWT토큰 응답/요청 (1) | 2023.12.02 |
[Front/React] FullCalender 라이브러리 커스터마이징 (0) | 2023.11.29 |
[Front/React] useRef (+ 실습예제) (1) | 2023.11.24 |