리엑트에서 input의 type을 file로 하면 파일을 업로드할 수 있습니다. 저희 프로젝트에서 이미지를 업로드가 필요하게 되어 관련 기능을 구현하게 되었습니다.
이미지 업로드 전에 유저가 자신이 올릴 이미지를 미리보게 하기 위해서
웹api FileReader 를 통해서 filereader를 통해서
const file = imgRef.current.files[0];
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
setImgFile(reader.result);
};
https://developer.mozilla.org/ko/docs/Web/API/FileReader
FileReader - Web API | MDN
FileReader 객체는 웹 애플리케이션이 비동기적으로 데이터를 읽기 위하여 읽을 파일을 가리키는File 혹은 Blob 객체를 이용해 파일의 내용을(혹은 raw data버퍼로) 읽고 사용자의 컴퓨터에 저장하는
developer.mozilla.org
하지만 예전 프로젝트에서 이미지를 여러장 올릴 경우 백엔드에서 압축을 할 경우 네트워크 비용도 높고, 유저 입장에서 업로드에 걸리는 시간도 너무 길었다고 느꼈다.
따라서 이미지 미리보기 전 단계에서 미리 encoding을 하면 시간이 분배가 되니깐 상대적으로 UX가 향상되지 않을까 생각하고 이를 구현하려고 합니다.
이때 Browser Image Compression 와 react-image-file-resizer을 쓰는데, 개인적으로 file 변환에 대한 가독성이 후자 라이브러리가 더 좋다고 느껴서 , 채택하였습니다.
npm i react-image-file-resizer
그리고 webp를 사용할 경우 png나 jpg에 비해서 압축시 파일 손실도 적고 성능향상을 노리기 쉽다고 생각해서 webp로 인코딩을 하려고 합니다.
참고한 글
https://velog.io/@lky5697/fast-images
(번역) HTML 이미지 최적화
원문 : https://www.builder.io/blog/fast-images여러분이 운영하는 멋있는 페이지가 있고 그 페이지에 배경 이미지를 추가한다고 가정해봅시다.잠시만요!이 방법이 성능에 최적화 되지 않은 방법이라는 사
velog.io
https://github.com/onurzorluer/react-image-file-resizer
GitHub - onurzorluer/react-image-file-resizer: Resize Local Images with React 🌄 🌅
Resize Local Images with React 🌄 🌅. Contribute to onurzorluer/react-image-file-resizer development by creating an account on GitHub.
github.com
https://developers.google.com/speed/webp?hl=ko
import { useState, useRef } from 'react';
import { ownerRegiImg } from '../../../assets/images';
import { webpImageIncoder } from '../../../utils';
const BusinessRegi = () => {
const [isRegistered, setIsRegistered] = useState(false);
const [imgRef, imgFile, setIncodedImg] = useImageConverter();
const imgRef = useRef<HTMLInputElement | null>(null);
const [imgFile, setImgFile] = useState<string | null>(null);
const setIncodedImg = async () => {
if (imgRef.current?.files) {
const file = imgRef.current.files[0];
const webpfile = await webpImageIncoder(file);
setImgFile(webpfile + ''); // unknown type을 string으로 변환
}
};
return (
<div>
{!isRegistered ? (
<div>
<input type="file" accept="image/*" onChange={setIncodedImg} ref={imgRef} />
<br />
<img src={imgFile ? imgFile : ownerRegiImg} alt="사업자 이미지" width="300px" object-fit="contain" />
<button onClick={() => setIsRegistered(true)}>이미지전송</button>
</div>
) : (
<div>
<div>상호 : 서버로 부터 받은 값 </div>
<div>숙소명 :서버로 부터 받은 값</div>
</div>
)}
</div>
);
};
export default BusinessRegi;
// webpImageIncoder 함수
import Resizer from 'react-image-file-resizer';
export const webpImageIncoder = (file: File) =>
new Promise((resolve) => {
Resizer.imageFileResizer(
file, // Is the file of the image which will resized.
800, // Is the maxWidth of the resized new image.
800, // Is the maxHeight of the resized new image.
'webp', // Is the compressFormat of the resized new image.
100, // Is the quality of the resized new image.
0, // Is the degree of clockwise rotation to apply to uploaded image.
(uri) => {
// Is the callBack function of the resized new image URI.
resolve(uri);
},
'base64',
);
});
라이브러리에 적힌 사용법대로 file을 최대 800,800 크기의 webp이미지로 변환하고 압축을 하지 않도록 하였다.
https://github.com/onurzorluer/react-image-file-resizer
GitHub - onurzorluer/react-image-file-resizer: Resize Local Images with React 🌄 🌅
Resize Local Images with React 🌄 🌅. Contribute to onurzorluer/react-image-file-resizer development by creating an account on GitHub.
github.com
라이브러리의 결과값이 Promise<unknown> 으로 반환되서 (실제 결과값의 typeof를 확인하면 string임에도)
아직 css는 적용하지 않았는데, 위 코드는 사업자등록을 하기전 이미지를 미리 보기 하는 코드입니다.
이미지를 업로드하게 하고, 그 이미지를 미리보기로 보여준 후에,
naver-cloud api를 통해서 사용가능한 사업자 등록증인지 확인하려는 코드로 가기전 우선 이미지 등록부터 구현중입니다.
이미지 업로드에 성공했네요. 변환된 파일을 console.log(webpfile)로 확인했을 떄 webp, base64로
인코딩이 잘 된것도 확인됩니다.
추가작업
이를 팀원과 같이 쓰기 위해서 커스텀 훅으로 만들고 싶습니다.
이때 ref 와 state , 그리고 이미지 파일을 변환해서 set하는 setIncodedImg 를 활용하면 다른 파일에서도 사용할 수 있는
커스텀 훅이 될 거 같습니다.
이름은 useImageConverter 로 작명하겠습니다.
import { useRef, useState } from 'react';
import { webpImageIncoder } from '../utils';
export const useImageConverter = () => {
const imgRef = useRef<HTMLInputElement | null>(null);
const [imgFile, setImgFile] = useState<string | null>(null);
const setIncodedImg = async () => {
if (imgRef.current?.files) {
const file = imgRef.current.files[0];
const webpfile = await webpImageIncoder(file);
setImgFile(webpfile + ''); // unknown type을 string으로 변환
}
};
return { imgRef, imgFile, setIncodedImg };
};
import { useState } from 'react';
import { ownerRegiImg } from '../../../assets/images';
import { useImageConverter } from '../../../hooks';
const BusinessRegi = () => {
const [isRegistered, setIsRegistered] = useState(false);
const { imgRef, imgFile, setIncodedImg } = useImageConverter();
return (
<div>
{!isRegistered ? (
<div>
<input type="file" accept="image/*" onChange={setIncodedImg} ref={imgRef} />
<br />
<img src={imgFile ? imgFile : ownerRegiImg} alt="사업자 이미지" width="300px" object-fit="contain" />
<button onClick={() => setIsRegistered(true)}>이미지전송</button>
</div>
) : (
<div>
<div>상호 : 서버로 부터 받은 값 </div>
<div>숙소명 :서버로 부터 받은 값</div>
</div>
)}
</div>
);
};
export default BusinessRegi;
이를 통해서 이미지를 미리보기를 구현하였습니다. (삭제까지 구현하려면 훅이 변경될 수도 있겠네요,,!?)
'프론트엔드' 카테고리의 다른 글
리엑트 useEffect 훅의 역할 및 실행 순서 학습 및 실험 (0) | 2023.10.02 |
---|---|
gitaction을 통한 빌드시 환경변수 적용하기 (0) | 2023.09.30 |
React에서 FullCalendar 조건에 따라서 달력 칸 style 주기(event여부에 따라서 className 주기) (0) | 2023.09.13 |
REACT TOSS PAYMENT를 활용해서 결제 기능 구현하기(테스트) (0) | 2023.09.03 |
react를 위한 docker파일 작성 (0) | 2023.09.03 |