[React] 이미지 압축해서 업로드하기

Sunghoman·2022년 12월 13일

React

목록 보기
5/5
post-thumbnail

오늘은 이미지 업로드 할 때 압축해서 올리는 방법 알아봅시당

module install

npm i browser-image-compression
yarn add browser-image-compression

module import

import imageCompression from "browser-image-compression";

component

// 이미지 담을 file state
const [boardImage, setBoardImage] = useState(null);
// 이미지 미리보기 state
const [uploadPreview, setUploadPreview] = useState([]);

...
return (
  <ImageUploadBox>
    <label htmlFor="image" ref={uploadBoxRef}>
      <div className="textBox">
        <h4>클릭하여 업로드</h4>
        <span>권장사항: 5MB 이하 고화질</span>
      </div>
      <BsUpload className="uploadBtn" />
    </label>
    <input type="file" accept="image/*" id="image" ref={imageRef} onChange={onChangeImage} />
    <ImagePreview>
      <img src={uploadPreview} alt="previewImage" />
    </ImagePreview>
  </ImageUploadBox>
)

기존의 이미지 업로드 함수

const onChangeImage = e => {
  setBoardImage(e.target.files[0]);
  let reader = new FileReader();
  if (e.target.files[0]) {
    reader.readAsDataURL(e.target.files[0]);
  }
  reader.onloadend = () => {
    const previewImgUrl = reader.result;
    if (previewImgUrl) {
      setUploadPreview(previewImgUrl);
    }
  };
};

이걸 라이브러리 써서 압축해봅시당

image resize

const imageCompress = async (e) => {
  let file = e.target?.files[0]
  const options = {
    maxSizeMB: 0.2, // 이미지 최대 용량
    maxWidthOrHeight: 1920, // 최대 넓이(혹은 높이)
    useWebWorker: true,
  };
  try {
    const compressedFile = await imageCompression(file, options);
    setBoardImage(compressedFile);
    const promise = imageCompression.getDataUrlFromFile(compressedFile);
    promise.then((result) => {
      setUploadPreview(result);
    })
  } catch (error) {
    console.log(error)
  }
};

라이브러리 덕분에 아주 간편하게 압축할 수 있읍니다.

이렇게 압축한 녀석을 살펴보면,

Blob {
  lastModified: 1594690440635
  name: "image.jpg"
  size: 158816
  type: "image/jpeg"
}

이런 식으로 나옵니다.
Blob은 JavaScript에서 이미지, 사운드, 비디오와 같은 멀티미디어 데이터를 다룰 때 사용합니다.
Binary Large Object

데이터의 크기(Byte)나 MIME Type을 알아내거나, 데이터 송수신을 위한 작은 Blob 객체로 나누는 작업 등에 사용함

File 객체도 name과 lastModifiedDate 속성이 포함된 Blob 객체임

암튼 이미지 압축을 진행했으니, Base64로 변환 할거임

왜용?

왜냐면 ASCII는 데이터를 전달하기에 안전하지 않다고 합디다.

특수문자를 제외한 64개의 안전한 출력 문자로 서버에 전송 및 DB 저장을 위해 변환해줍니당

이미지 깨짐, 동영상 짤림 등을 방지하기 위해 안전한 문자로 바꿔서 서버에 전송하는거임

Base64 변환

const imageCompress = async (e) => {
  let file = e.target?.files[0]
  const options = {
    maxSizeMB: 0.2, // 이미지 최대 용량
    maxWidthOrHeight: 1920, // 최대 넓이(혹은 높이)
    useWebWorker: true,
  };
  try {
    const compressedFile = await imageCompression(file, options);
    setBoardImage(compressedFile);
    const promise = imageCompression.getDataUrlFromFile(compressedFile);
    promise.then((result) => {
      setUploadPreview(result);
    })
    
    // FileReader
    const reader = new FileReader();
    reader.readAsDataURL(compressedFile);
    reader.onloadend = () => {
    	const base64data = reader.result;
      	formDataHandler(base64data); // 이 함수는 밑에서 설명
    }
  } catch (error) {
    console.log(error)
  }
};

이제 이걸 formData로 만들어서 서버에 전송까지 해봅시다요

const formDataHandler = async(dataURI) => {
  const byteString = atob(dataURI.split(",")[1]);
  const ab = new ArrayBuffer(byteString.length);
  const ia = new Uint8Array(ab);
  for (let i = 0; i < byteString.length; i++) {
    ia[i] = byteString.charCodeAt(i);
  }
  const blob = new Blob([ia], {
    type: "image/jpeg"
  });
  const file = new File([blob], "image.jpg");
  
  const formData = new FormData();
  formData.append("representative_avatar", file);
  
  try {
    const response = await {서버 전송하는 API}(formData);
    console.log(response)
  } catch (error) {
    console.log(error);
  }
}

마지막으로 axios 전송 전에 header에 보내는 데이터의 Type을 지정해주면 끝!

createBoard: (payload) => axios.post(`${BASE_URL}/boards`, payload, {
  headers: {
    Authorization: getCookieToken("access-token"),
    // header 부분에 아래와 같이 data type을 지정해줍니다.
    "Content-Type": "multipart/form-data",
  },
}),
profile
쉽게만 살아가면 개재밌어 빙고

1개의 댓글

comment-user-thumbnail
2022년 12월 25일

어라라,,,, 이미지 압축하는 법 공부하다가 여기까지 왔네요 @)---- 잇님 잘 보고 갑니다 ^^*

답글 달기