본문 바로가기
💻CODING/react. vue

[react] browser-image-compression 이미지 리사이징 적용

by 코딩하는 갓디노 2022. 11. 4.

[react] browser-image-compression 이미지 리사이징 적용

 

커뮤니티를 구현하다가 이미지를 별도의 처리 없이 서버로 보낸 후, 가져오다 보니 로딩 시간이 많이 걸려
프론트에서 이미지를 압축한 뒤 서버로 보내어 로딩속도를 개선했습니다. 

 

구현방법

  • react, redux-toolkit 사용
  • 자바스크립트 imageㄹ 리사이징 압축 라이브러리, browser-image-compression를 사용
  • 총 3개의 이미지까지 보낼 수 있어서 for문을 이용

 

문제점

Promise로 만들어진 browser-image-compression 라이브러리를 그대로 이용하여
actionImgCompress 함수의 리턴값을 변수에 저장하니 결과값이
array가 아닌 promise {<pending>}[[prototype]]: promise[[promise state]]: "fulfilled"[[promiseresult]] 가 나와서 
이미지 Blob 파일이 promiseresult에 저장이 되어있었습니다. 

해결을 위하여 actionImgCompress(e.target.files[i]) 함수를 부를 때

async, await를 사용하니 바로 Blob 파일이 출력되었습니다. 

 

input 코드

const PhotoCard = ({ onBringImg }) => {
  return (
    <>
      <input
        name="camera"
        type="file"
        multiple
        id="camera-image"
        accept="image/*"
        onChange={(e) => onBringImg(e)}
        className="hidden"
      />
      <label htmlFor="camera-image"">
        카메라
      </label>
    </>
  );
};

export default PhotoCard;

 

browser-image-compression 코드

import imageCompression from 'browser-image-compression';

const actionImgCompress = async (fileSrc) => { //이미지 압축
        const options = { //압축 옵션
            maxSizeMB: 0.6,
            maxWidthOrHeight: 1200,
            useWebWorker: true,
        }
        try {
            const compressedFile = await imageCompression(fileSrc, options)
            return compressedFile
        } catch (error) {
            console.log(error);
        }
    }

 

input onChange 함수

const [postData, setPostData] = useState({ title: '', contents: '', camera: [] }) //서버로 보내기 위한 데이터
const [loadedImage, setLoadedImage] = useState([]) //화면 미리보기를 위한 데이터

const onBringImg = async (e) => {
    let fileURLs = []
    let filesLength = loadedImage.length > 0 ? 3 - loadedImage.length : e.target.files.length > 3 ? 3 : e.target.files.length
    let cameraArr = []
    for (let i = 0; i < filesLength; i++) {
        const compressed = await actionImgCompress(e.target.files[i]) //압축 처리
        cameraArr.push(compressed) //압축된 파일 배열로 보관 후 서버 보내기
        setPostData({ ...postData, camera: [...postData.camera, ...cameraArr] })
        let reader = new FileReader() // FileReader API로 이미지 인식  
        reader.readAsDataURL(e.target.files[i]) //reader에게 file을 먼저 읽히고
        reader.onload = () => { // 사진 올리고 나서 처리하는 event
            fileURLs[i] = reader.result
            setLoadedImage([...loadedImage, ...fileURLs]) //데이터를 img src 넣어 이미지 미리보기 가능
        }
    }
}

 

서버 보내기 코드

파일을 여러개 보냈을 때, params.files.forEach((file) => formdata.append("files", file)) 으로 보내다 보니, Blob을 그대로 보냈을때 서버에서 받을 때 같은 파일로 인식하여 동일한 파일 3개가 만들어졌습니다. 

코드 파라미터에 file.name을 더 보내어 문제를 해결하였습니다. 

export const saveMyPost = createAsyncThunk("app/saveMyPost",
    async (params) => {
        const config = { headers: { "content-type": "multipart/form-data" } }
        let formdata = new FormData()
        formdata.append("userId", params.userId)
        formdata.append("title", params.title)
        formdata.append("content", params.content)
        params.files.forEach((file) => formdata.append("files", file, file.name)) //file.name 추가
        const res = await api.post(`주소`, formdata, config)
        return res.data
    })

 

반응형

댓글