사이드 프로젝트 배포 후 모바일 디바이스에서 서비스를 이용할 때 게시글 생성 중 이미지 용량이 클 경우 하단 내용 입력 창 딜레이가 커지는 이슈가 발생했다.
또한 게시글 목록에서 서버에서 이미지를 받은 후 뷰포트에 그리는 시간이 길어지면서 이미지가 뚝뚝 끊기면서 로딩되는 현상도 발생했다.
이미지 업로드 용량을 10MB로 제한하고 있었다. 그러다 보니 10MB 이하의 고화질 이미지를 등록할 경우 화면 전체가 느려지면서 입력창 딜레이가 심해졌고, 프론트와 백엔드 모두 이미지 압축을 하고 있지 않아서 리스트를 불러올 때도 딜레이가 길었던 것이다.
따라서 프론트엔드/백엔드 모두 이미지 최적화를 하기로 했다.
browser-image-compression
라이브러리를 사용하여 이미지를 리사이징하기로 했다.
선택 이유
code
const handleAddImg = async (e: ChangeEvent<HTMLInputElement>) => {
const target = e.target.files;
if (target && target.length) {
if (target.length + previewList.length > DISK_IMG_MAX_LENGTH) {
// 이미지 최대 개수 제한
window.alert(
`사진은 최대 ${DISK_IMG_MAX_LENGTH}개까지 등록할 수 있습니다.`
);
} else {
const newFiles: File[] = Array.from(target);
newFiles.map(async (file) => {
if (file.size > IMG_MAX_SIZE) {
// 이미지 최대 용량 제한
window.alert(
`${Math.round(
IMG_MAX_SIZE / 1000000
)}MB 이하의 사진만 등록할 수 있습니다.`
);
} else {
const options = {
maxSizeMB: 2, // 이미지 최대 용량
maxWidthOrHeight: 900, // 최대 넓이(혹은 높이)
fileType: "image/jpeg",
useWebWorker: true,
};
const previewOptions = {
maxSizeMB: 0.3, // 이미지 최대 용량
maxWidthOrHeight: 900, // 최대 넓이(혹은 높이)
fileType: "image/jpeg",
useWebWorker: true,
};
try {
const compressedBlob = await imageCompression(file, options);
const compressedFile = new File([compressedBlob], file.name, {
type: file.type,
});
setFiles((prev) => [...prev, compressedFile]);
const compressedPreview = await imageCompression(
file,
previewOptions
);
const reader = new FileReader();
reader.readAsDataURL(compressedPreview);
reader.onloadend = () => {
const previewImgUrl = reader.result;
setPreviewList((prev: DiskImgType[]) => [
...prev,
{ imgId: "new", imgUrl: previewImgUrl as string },
]);
if (!previewList.length) {
setMainImg(previewImgUrl as string);
}
};
} catch (err) {
window.alert("사진을 불러올 수 없습니다.");
throw err;
}
}
});
}
}
};
✅ 이미지 로딩 시간이 약 절반 가량 줄었다.
정식 배포 후 팀원들의 지인 위주로 유저 테스트 진행 중 업로드할 이미지를 선택하면 간헐적으로 사진이 회전+축소된다는 버그 제보를 받았다.
출처 : https://github.com/Donaldcwl/browser-image-compression/issues/189
(제보 받은 사진이 개인적인 사진이라 동일한 이슈로 대체함)
자세히 물어보니 다음과 같은 상황이었다.
세 번째에서 당황.. 이미지크기 or 용량 or 확장자 이슈일 거라고 예측했지만 같은 이미지를 다른 갤럭시 기기에서 올리면 정상적으로 업로드 되었기 때문이다. 크롬 브라우저에서도 잘 업로드 되었다.
이미지 문제가 아니라면 브라우저 또는 안드로이드 기기에서 발생하는 버그가 아닐까 해서 구글링했는데 이런 문제를 겪는 케이스를 찾지 못했다.
그래서 라이브러리의 GitHub에 찾아가서 issue 탭에 가보니 나와 같은 문제를 겪은 issue가 있었다. (상단 사진과 url)
안드로이드 기기에서 간헐적으로 발생하는 이슈 같았고 아래 개발자의 comment를 보니 해결된 것 같지 않아 라이브러리를 교체하기로 했다.
(작년에 만든 프로젝트에서도 사용한 라이브러리였고 당시 업로드 관련 어떤 버그 제보도 없었다. 위 이슈도 올해 4월 20일에 올라온 걸 보면 올해부터 버그가 생긴 걸수도..?)
browser-image-compression
대신 react-image-file-resizer
라이브러리를 사용했다.
선택 이유
browser-image-compression
다음으로 많은 사용자 + 최근 업데이트code
const resizeFile = (file: File) =>
new Promise((res) => {
Resizer.imageFileResizer(
file, // target file
1500, // maxWidth
1500, // maxHeight
"JPEG", // compressFormat : Can be either JPEG, PNG or WEBP.
80, // quality : 0 and 100. Used for the JPEG compression
0, // rotation
(uri) => res(uri), // responseUriFunc
"file" // outputType : Can be either base64, blob or file.(Default type is base64)
);
});
const resizePreview = (file: File) =>
new Promise((res) => {
Resizer.imageFileResizer(
file,
1500,
1500,
"JPEG",
60,
0,
(uri) => res(uri),
"base64"
);
});
const handleAddImg = async (e: ChangeEvent<HTMLInputElement>) => {
const target = e.target.files;
if (target && target.length) {
if (target.length + previewList.length > DISK_IMG_MAX_LENGTH) {
// 이미지 최대 개수 제한
window.alert(
`사진은 최대 ${DISK_IMG_MAX_LENGTH}개까지 등록할 수 있습니다.`
);
} else {
const newFiles: File[] = Array.from(target);
newFiles.map(async (file) => {
if (file.size > IMG_MAX_SIZE) {
// 이미지 최대 용량 제한
window.alert(
`${Math.round(
IMG_MAX_SIZE / 1000000
)}MB 이하의 사진만 등록할 수 있습니다.`
);
} else {
try {
const compressedFile = (await resizeFile(file)) as File;
const compressedPreview = (await resizePreview(file)) as string;
setFiles((prev) => [...prev, compressedFile]);
setPreviewList((prev: DiskImgType[]) => [
...prev,
{ imgId: "new", imgUrl: compressedPreview },
]);
} catch (err) {
window.alert("사진을 불러올 수 없습니다.");
throw err;
}
}
});
}
}
};
react-image-file-resizer
는 이미지 최대 용량이 아닌 quality option을 %로 정해야 했다. 이미지가 작으면 quality 100이어도 화질 저하가 심해 크기를 1500으로 키우는 대신 quality를 80으로 낮췄다.✅ 라이브러리 변경 후 시각적으로 큰 차이는 없었다.
✅ 기존 리사이징보다 1.6~2.9 배 로딩 속도가 빨라졌다. (게시글 목록 기준)
✅ 기존 리사이징 보다 약 1/4 이미지 용량을 줄였다. (업로드 페이지 기준)
변경된 라이브러리 또한 유저 피드백을 기다려봐야겠지만 아직까진 문제없이 사용 중이다.
오히려 화질이 손상되지 않으면서 이미지 용량이 줄어 로딩 속도가 개선되었다.
https://www.npmjs.com/package/browser-image-compression
browser-image-compression issue
https://github.com/onurzorluer/react-image-file-resizer