728x90

리엑트에서 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;

이를 통해서 이미지를 미리보기를 구현하였습니다.  (삭제까지 구현하려면 훅이 변경될 수도 있겠네요,,!?)

+ Recent posts