fbpx

이미지 리사이징, 이미지 용량 줄이기 (2) (부제 : 어떤 기준으로 리사이징해야할까? )

플로우 개발 스토리 2월 14, 2020
dellose

author:

이미지 리사이징, 이미지 용량 줄이기 (2) (부제 : 어떤 기준으로 리사이징해야할까? )

글을 읽기 전에

이미지 리사이징, 이미지 용량 줄이기 (1) (feat. JavaScript, HTML5 canvas) 포스트를 확인하시면 javascript 환경에서 기본적인 이미지 리사이징 방법을 알 수 있습니다.


원본이미지

압축이미지

Canvas의 리사이징

5575 x 3923, 2.37MB이미지가 있다고 가정해보겠습니다. 한 번 다운로드 시에는 무리가 없는 이미지 용량으로 보이지만 수십 번 이미지를 다운로드하게 되면 더 많은 리소스가 요구됩니다. 해당 리소스를 줄이기 위하여 이미지 리사이징을 시도해보겠습니다. Canvas는 가로, 세로 px를 정하고 그 위에 이미지를 그리는 방식이기 때문에 px 기준으로 이미지가 리사이징됩니다. 보통 최대 px를 기준으로 비례하여 이미지를 리사이징합니다. 예를 들어, 위 이미지를 최대 px 400px기준으로 이미지를 리사이징한다고 가정하고 코드를 작성해보겠습니다.

//위 소스코드 생략
var maxSize = 400; //최대px 400px 기준
var width = image.width; //5575px
var height = image.height; //3923px
if (width > height) { 
    if (width > maxSize) {
        height *= maxSize / width;
        width = maxSize;
    }
} else {
    if (height > maxSize) {
        width *= maxSize / height;
        height = maxSize;
     }
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);

해당 로직을 타게 되면 비례식에 의하여 width는 400px, height는 281px(3923*400/5575)이 됩니다.

canvas로 압축된 이미지의 가로는 5575px에서 400px이 되고 세로는 3923px에서 281px이 되었습니다. 2.37MB였던 이미지의 용량은 몇이 되었을까요? 가로세로가 각각 약 1/14만큼 줄었고 제곱으로 치면 약 1/196가 됩니다. 2.37MB에서 약 1/196 정도 용량이 줄었을 것으로 예상이 됩니다.

KB로 계산하면 12.38KB가 됩니다. 하지만 실제로 canvas를 활용하여 이미지를 줄였을 때 가로세로 px은 400 x 281 맞게 리사이징되었지만 용량은 30.8KB가 되었습니다. 왜 그럴까요? 이는 dpi(해상도)와 각 확장자별 압축방식의 차이로 발생합니다. 이미지의 압축방식 등에 대한 내용은 주제에 벗어나는 내용이니 차후에 좀 더 알아보면 좋을 것 같습니다. 일단, 최대 px을 기준으로 canvas 리사이징할 때 용량을 충분히 줄일 수 있다는 것에 대하여 알게 되었습니다. 하지만 좀 더 생각해보아야 할 부분이 있습니다.


어떤 기준으로 리사이징해야 할까요?

절대적인 기준으로 리사이징하는 과제라면 고려해보지 않아도 될 문제이지만 썸네일 처리와 같이 다양한 이미지의 용량을 줄이는 것이 목적인 경우 고려해볼 부분들이 좀 더 있습니다.

Q. 만약 가로세로가 400px보다 작은 이미지가 있다면 어떠할까요?
A. 굳이 리사이징할 필요가 없어 보입니다.

Q. 만약 기준과 근접한 크기의 401 x 401 이미지가 있다면 어떠할까요?
A. 가로세로가 400px보다 크니까 리사이징해도 좋을 것 같습니다만 정작 리사이징을 해보면 이미지가 심각하게 깨지는 경우를 만나게 됩니다.

Q. 만약 한쪽 비율이 과하게 높은 1600 x 20 이미지가 있다면 어떠할까요?
A. 400px 기준으로 줄였다가는 400 x 5 이미지를 만나게 되고 역시 이미지가 심각하게 깨지는 경우를 만나게 됩니다.

Q. 만약 1000 x 800 이미지가 용량이 20KB밖에 안 되는 용량이 작은 이미지가 있다면 어떠할까요?
A. 400px 기준으로 줄여서 더 용량이 작아지는 이미지를 만들어낼 것입니다.

Q. 만약 399 x 399 이미지가 용량이 1.5MB 정도 되는 용량이 큰 이미지가 있다면 어떠할까요?
A. 400px 기준으로 줄여야 하니 리사이징이 적용되지 않을 것입니다.

이를 통해 알 수 있는 문제는 용량을 줄이는 것이 목적일 때 길이(px) 기준으로 줄이는 방법은 좋지 않다는 것입니다!


길이 기준을 용량 기준으로 바꾸는 방법

고민을 해보았습니다. 정확하게 용량을 맞출 수 있는 방법은 없지만 대략적으로는 맞출 수 있을 것이라고 생각했습니다. 5575 x 3923, 2.37MB 이미지를 다시 가져와보겠습니다. 썸네일 처리를 하기 위해 최대 px 400px이 아닌 용량 100KB로 기준을 잡아보겠습니다. 2.37MB(2426KB)가 100KB가 되기 위해서는 1/24.26만큼 줄여야 합니다.

단순하게 가로 세로 루트 값 약 4.9씩 줄이면 됩니다. 1137(5575/4.9) x 800(3923/4.9) 을 기준으로 canvas 리사이징을 진행합니다. 이렇게 했을 때 2.37MB의 이미지는 100KB가 될까요? 실제로 진행했을 때 135KB의 이미지를 얻을 수 있습니다. 100KB 기준으로 길이 비례를 계산하여 canvas 리사이징을 진행하면 100KB에 가까운 이미지를 얻을 수 있습니다. 위에 말했던 이미지 압축방식과 dpi에 따라 용량은 정확하지 않을 수 있기 때문에 100KB 가깝게 압축했다는 것만으로 좋은 압축이 될 수 있을 것 같습니다.

추가로, 100KB 기준으로 리사이징 유무를 결정하게 되면 101KB 일 때 쓸데없이 리사이징하는 경우가 생길 수 있습니다. 또, 이때 이미지가 깨지는 경우가 생길 수 있습니다. 그래서 리사이징 유무의 기준이 되는 용량은 1MB로 하고 리사이징을 한다면 100KB로 리사이징을 시도하는 로직을 만들었습니다.

function getThumbImgFile(image, file){
	var canvas = document.createElement("canvas");
	var base_size	= 1024000; //1MB (썸네일 작업 유무 기준 사이즈)
	var comp_size	= 102400;  //100KB (썸네일 작업 결과물 사이즈, 50~200KB 수준으로 압축됨)
	var width 	 	= image.width;
	var height 		= image.height;
	var size 		= file.size;

	if(size > base_size){
		var ratio = Math.ceil(Math.sqrt((size / comp_size), 2));
          	width 	  = image.width / ratio,
		height 	  = image.height / ratio;
		canvas.width = width;
		canvas.height = height;
		canvas.getContext("2d").drawImage(image, 0, 0, width, height);
		var tmpThumbFile= dataURItoBlob(canvas.toDataURL("image/png"))); //dataURLtoBlob 부분은 이전 포스팅 참조
			return tmpThumbFile;
		} else {
			return "";
		}
}

이와 같이 이미지 리사이징을 진행하면 좀 더 효과적으로 이미지 리사이징을 할 수 있습니다.

정확한 용량으로 리사이징하거나 더 좋은 기준으로 리사이징할 수 있다면 방법을 공유해주세요. 방법은 다양하지만 간단하게 썸네일 파일을 만들고자 할 때 위와 같은 로직으로 접근한다면 좋은 썸네일을 얻을 수 있을 것 같습니다!

플로우는 해당 로직을 2019년 하반기부터 적용하여 원활한 서비스 환경을 제공하고 있습니다!