이전 프로젝트에서 FileReader와 File을 활용해서 서버와 이미지를 전송하는 과정을 가졌습니다. 당시에는 큰 고민 사항 없이 파일을 주고 받는 것에 만족을 하였는데, 이번에는 이미지 자르기, 파일 변환, 파일 압축 등을 고려해서 코드를 작성해보고자 하였습니다. 이때 Base64 인코딩, File, Blob에 대한 이해가 더 필요하다 여겨 정리를 하게 되었습니다.
base64 인코딩은 버퍼의 왼쪽부터 6bit 단위로 잘라서 Base64 테이블의 ACII 문자로 변환합니다.
ASCII 코드는 8bit를 사용하지만, base64 인코딩은 6bit 만을 사용합니다. 6bit 당 2bit의 OverHead가 발생하여 33% 가량 데이터의 크기가 증가하게 됩니다. 그렇다면 효율성에서 좋지 않은데 이를 사용하는 이유가 무엇일까요? 이는 Binary 데이터를 손실 없이 모두 영문자로 변환하기 위함입니다.
기본적으로 HTML은 ASCII 코드로 되어있습니다. HTML에서 표현할 수 없는 Binary 데이터(이미지, 비디오)를 표현시키기 위하여 Base64 인코딩을 진행하게 됩니다. 이를 통해 총량이 늘어나더라도 데이터를 한번에 요청할 수 있습니다.
더불어 JSON, HTML은 모두 문자열로 데이터를 주고받습니다. 이때 Base64로 값을 주고 받을 수 있습니다.
만약 Base64가 6자리로 나누어 떨어지지 않을 경우, Padding 값을 넣어서 강제로 6자리로 맞춥니다. 이때 끝 자리는 ‘=’ 표시 됩니다.
Blob
JavaScript에서 Blob(Binary Large Object)은 이미지, 사운드, 비디오와 같은 멀티미디어 데이터를 다룰 때 사용할 수 있습니다. Blob은 Binary 데이터로 되어 있으며, size 값과 type 값을 가지고 있습니다. type은 MIME 타입으로 해당 파일의 형식을 알려주고 있습니다.
File
JavaScript에서 File 형식은 prototype으로 Blob을 받고 있습니다. 즉 Blob 형식의 확장자라고 할 수 있습니다. 따라서 Blob을 사용할 수 있는 모든 맥락에서 사용할 수 있습니다. FileReader, URL.createObjectURL(),는 Blob과 File을 모두 허용합니다.
코드 구현하기
이미지 자르기 기능은 React-cropper을 활용해서 구현하였습니다.
이미지 압축은 browser-image-compression을 활용하여 구현하였습니다.
CODE / EXAMPLE
consthandleProfile= (e:ChangeEvent<HTMLInputElement>) => {let file:File|undefined;if (e.target.files) {// input의 이미지 파일을 가지고 옵니다. file = e.target.files[0];// Cropper 라이브러리를 활성화 시키는 state 값을 true로 변경합니다.setIsViewImageCropper(true);constreader=newFileReader();// file 객체를 Base64로 인코딩을 진행하여 web 상에서 이미지 파일을 볼 수 있도록 합니다. reader.readAsDataURL(file);returnnewPromise<void
Cropper 라이브러리를 활용해서 자른 이미지를 file로 가지고 오는 코드를 작성합니다.
CODE / EXAMPLE
constgetCropData=async () => {try {constcropper= cropperRef.current?.cropper;if (cropper) {// 파일 변환 과정 동안 loding 창을 보여줍니다.setIsLoading(true);// cropper Canvas img를 base64로 변환합니다.constimage= cropper.getCroppedCanvas().toDataURL();// fileRedear를 통해 보여주는 이미지를 state로 저장합니다.setImage(image);// file 형태로 변환constcropfile=awaitfetch(cropper.getCroppedCanvas().
휴대폰에서 사진을 업로드 하는 과정에서 대부분의 이미지가 5mb를 넘기 때문에 browser-image-compression 라이브러리를 적용해서 이미지의 크기를 약 10% 줄일 수 있었습니다. 추가로 jpeg 확장자를 사용하여 손실 압축 방식을 통해 이미지 크기를 줄일 수 있었습니다. 하지만 이미지 손실을 감안해야 하는 부분과 이미지 크기를 효율적으로 줄이지 못한다는 부분이 아쉬웠습니다. 이에 대한 해결책으로 Google에서 개발한 WebP를 적용해서 이미지 크기를 줄이는 방법을 도입하고자 합니다.
CONVERSATION / NOTES
읽은 뒤에 남기는 메모.
질문이나 다른 경험이 있다면 이어서 남겨주세요. 좋은 대화는 다음 글의 출발점이 됩니다.