이미지 리사이징 로직 리팩토링

이예빈·2023년 2월 3일
0
post-thumbnail

동네북 프로젝트를 진행하면서 이미지 리사이징 로직을 추가했었는데 시간에 쫓겨 제대로 이해하지 못하고 진행했던 것 같아서 리팩토링을 진행해보았다.

기존에는 이미지 사이즈가 400px이 넘어가면 400px로 최대 크기를 지정하여 리사이징을 진행하는 방식으로 진행했었다. 하지만 이미지의 가로 세로 비율 차이가 심각하게 크다면? 이미지 사이즈가 401px로 살짝 넘어간다면? 그래도 똑같은 기준으로 리사이징이 진행될텐데 이미지가 심각하게 깨지지 않을까 하는 생각이 들었다.

그럴 일은 잘 없을 것 같긴 하지만 400px이 넘지 않는데 용량이 mb단위로 큰 고화질의 사진이라면? 리사이징 대상이 되지 않아 큰 용량이 그대로 서버로 넘어가게 될 것이다.

대참사

이미지 리사이징을 수행한 가장 큰 이유는 서버 리소스 부담을 줄여주기 위해서였다. 그렇다면 기준을 길이 기준이 아닌 용량기준으로 하면 되지 않을까?

400px을 기준으로 리사이징을 진행했을 때 가로세로가 적정한 크기의 이미지의 크기는 대략 40kb정도 선이었다. 그래서 50kb정도로 맞추려고 생각을 했는데 기준을 50kb로만 설정하면 51kb의 이미지가 첨부되었을 때 리사이징되면서 이미지가 깨질 수 있을 것이다.

따라서 전송하려는 파일의 최대 크기를 200kb로 잡고 200kb이상인 경우 50kb 선으로 리사이징을 진행해보았다.

const MAX_SIZE = 204_800; // 200KB
const COMP_SIZE = 51_200; // 50KB

기준으로 사용할 파일 용량을 byte 단위로 하여 변수에 담아준다.

예를 들어 3MB의 이미지 파일을 50kb로 줄이기 위해서는 약 1/60 정도로 줄여야 한다. 그렇다면 가로 세로 길이를 루트 60으로 나눠주면 될 것 같다.

// 리팩토링 전
const resizeImage = (imgEl: HTMLImageElement) => {
	const MAX_SIZE = 400;
	const canvas = document.createElement('canvas');
	let width = imgEl.width;
	let height = imgEl.height;
	if (width > height) {
		if (width > MAX_SIZE) {
			height *= MAX_SIZE / width;
			width = MAX_SIZE;
		}
	} else {
		if (height > MAX_SIZE) {
			width *= MAX_SIZE / height;
			height = MAX_SIZE;
		}
	}
	canvas.width = width;
	canvas.height = height;
	canvas.getContext('2d')?.drawImage(imgEl, 0, 0, width, height);
	return canvas.toDataURL('image/jpeg');
};

// 리팩토링 후
const resizeImage = (imgEl: HTMLImageElement, fileSize: number) => {
	const canvas = document.createElement('canvas');
	const ratio = Math.sqrt(fileSize / COMP_SIZE);
	let width = Math.ceil((imgEl.width /= ratio));
	let height = Math.ceil((imgEl.height /= ratio));
	canvas.width = width;
	canvas.height = height;
	canvas.getContext('2d')?.drawImage(imgEl, 0, 0, width, height);
	return canvas.toDataURL('image/jpeg');
};

코드가 훨씬 간결해졌다.

2.7MB의 파일 크기가 58.7KB로 리사이징에 성공했다..!

파일의 크기가 정확히 50kb로 맞춰지지 않는 것은 이미지 압축 방식과 dpi에 따라 일정하게 나타나지 않을 수 있기 때문이다.

맨 위에서 대참사가 났던 긴 사진도 크게 깨지지 않고 올라간 것을 확인할 수 있었다. (원래 깨짐이 심한 이미지라 많이 깨져 보인다...;;)

profile
temporary potato

0개의 댓글