srcset을 사용한 이미지 최적화 처리 오류 해결

부루베릐·2023년 2월 12일
0

TIL

목록 보기
6/19

회사에서 내가 담당하는 서비스의 이벤트 마케팅 페이지를 만드는 업무가 있었다. 별 일 아니라고 생각했는데, 내 생각과 달리 예상치 못한 이슈가 있어 해결하는 과정에서 재밌는 경험을 했었다.

문제 상황

화면에 따라 이미지 해상도가 변하지 않는다...

이벤트 페이지의 메인 이미지의 파일 종류는 해상도에 따라 낮은 해상도(x1)와 높은 해상도(x2) 두 가지로 나뉜다. 화면의 너비를 기준으로 너비가 일정 기준(768px) 이하면 낮은 해상도의 이미지를, 이상이면 높은 해상도의 이미지를 화면에 표시하려 한다.

분명 내 컴퓨터에서는 잘 되었다고 생각해서 배포까지 진행했는데, 알고 보니 두 가지 문제가 있었다.

  • 화면을 늘였다 줄였다 해도 같은 해상도의 이미지만을 받아 온다.

  • 맥북의 경우 고해상도 2x 이미지를, 엘지 그램은 저해상도 1x 이미지만을 받아 온다.

이 문제가 나타난 것은 화면 크기에 따른 이미지 최적화가 제대로 이루어지지 않았다는 것을 의미한다.

지금 이미지 최적화 코드는 다음과 같이 img 태그의 srcset 속성을 사용하고 있다.

<!-- src 속성은 srcset 속성을 사용할 수 없는 환경에서 동작한다. -->

<img v-if="mq === 'sm'"
  class="main_graphic img-100"
  :src="resource('mobile/main_graphic@1x.png')"
  :srcset="resourceJoin([
	{path: resource('mobile/main_graphic@1x.png'), x: 1}, 
	{path: resource('mobile/main_graphic@2x.png'), x: 2}
  ])"
>
<img v-else
  class="main_graphic img-500"
  :src="resource('web/main_graphic@1x.png')"
  :srcset="resourceJoin([
	{path: resource('web/main_graphic@1x.png'), x: 1}, 
	{path: resource('web/main_graphic@2x.png'), x: 2}
  ])"
>

클래스명 img-100과 img-500은 각각 모바일과 웹 환경일 때 이미지 태그의 크기에 대한 스타일을 의미한다. 웹과 모바일 환경을 나누는 기준은 화면의 너비이며, 이를 계산해주는 mq라는 변수의 값에 따라 달라진다. 각 환경에 맞춘 이미지를 따로 준비하여 웹 환경일 때는 웹에 맞춘 이미지를 보여주고 모바일일 때는 모바일에 맞춘 이미지를 보여주도록 한다. 그러나, 현재는 화면의 너비가 달라져도 똑같은 해상도의 이미지만을 받아 온다.

원인 분석 1

디바이스 너비 계산 로직의 오류?

앞서 엘지 그램일 때 저해상도의 이미지가 출력되고, 맥북일 때도 화면 너비와 상관없이 똑같은 고해상도의 이미지가 나온다고 했다. 그렇다면 화면 너비가 제대로 계산되지 않았던 게 아닐까?

화면 너비 계산 로직은 다음과 같다.

mounted() {
  this.promotion = window.promotion

  if (window.visualViewport) {
    this.mq = this.breakpoint(window.visualViewport.width)
  } else {
    this.mq = this.breakpoint(window.innerWidth)
  }
	// ...
}

methods: {
	breakpoint(vWidth) {
	  if (768 < vWidth) {
	    return 'lg'
	  } else {
	    return 'sm'
	  }
	},
	// ...
}

window.visualViewport 속성 혹은 window.innerWidth 속성을 통해 브라우저 너비를 계산하는 로직이다. 이 두 속성이 제대로 계산되지 않는다면 너비에 따른 이미지 변화가 제대로 이루어지지 않았을 수 있다.

일단 크롬으로 테스트해본 결과 엘지 그램과 맥북 두 환경 모두에서 window.visualViewport.width가 불러와지지 않는 경우는 없었다. canIUse에 따르면 IE일 때와 타 브라우저의 예전 버전의 경우에는 해당 속성이 지원되지 않는 경우가 있지만, 같은 최신 크롬에서는 디바이스 상관 없이 화면 너비는 제대로 계산된다. 따라서 이 가설 말고 다른 가능성을 생각해보도록 한다.

원인 분석 2

범인은 기존 코드의 srcset!

디바이스의 너비는 엘지 그램과 맥북 둘 다 잘 받아와지는 것 같다. 따라서 디바이스 너비 자체는 문제가 아니다. 그럼 이미지를 받아오는 srcset에 문제가 있을 수 있지 않을까?

srcset 로직을 다시 한 번 더 보자.

<img v-else
  class="main_graphic img-500"
  :src="resource('web/main_graphic@1x.png')"
  :srcset="resourceJoin([
		{path: resource('web/main_graphic@1x.png'), x: 1}, 
		{path: resource('web/main_graphic@2x.png'), x: 2}
	])"
>

resourceJoin()은 Vue 메서드이다. 이 친구는 srcset에 들어갈 속성을 string으로 가공하는 역할을 한다.

resourceJoin(pathList) {
  return pathList.map(x => `${x.path} ${x.x}x`).join(', ')
},

resourceJoin() 함수를 거쳐 나오는 srcset 값은 다음과 같다.

:srcset="web/main_graphic@1x.png 1x, web/main_graphic@2x.png 2x"

srcsetx 값은 디바이스의 DPR(devicePixelRatio)이다.

DPR이란?

DPR이란 1인치당 CSS 기준 픽셀 수 대비 1인치당 디바이스의 실제 픽셀 수를 의미한다. 디바이스의 해상도가 높아질수록 DPR의 값은 커진다.

위의 코드의 의도는 화면의 해상도에 따라 저해상도외 고해상도 이미지를 구분하여 화면에 띄워주는 것일 테다. DPR 값이 1이면 1x에 해당하는 이미지(여기서는 web/main_graphic@1x.png 경로에 있는 이미지)를, 2이면 2x에 해당하는 이미지(web/main_graphic@2x.png 경로에 있는 이미지)를 불러오는 로직이다. 이를 통해서 화면의 크기나 해상도에 따라서 얼마든지 최적화된 이미지 파일을 불러올 수 있다.

문제는 이렇게 했을 때 엘지 그램 컴퓨터의 경우에는 화면이 충분히 넓은데도 불구하고 해상도가 낮은 이미지만 화면에 노출된다는 것이었다. srcset에서 이미지를 받아오는 기준이 화면 너비가 아닌 DPR이라 문제가 된 걸까?

srcset의 기준이 되었던 DPR값을 살펴보도록 하자. 자바스크립트를 활용하여 이 값을 알 수 있는데, 다음과 같이 콘솔에 입력해 본다.

window.devicePixelRatio

엘지 그램의 해당 값은 1이지만, 맥북의 값은 2이다. 즉, 화면의 크기가 아닌 디바이스의 DPR 값에 따라 다른 해상도의 이미지를 받아왔으므로 엘지 그램에서는 계속해서 낮은 해상도 이미지를 받아왔던 것이다! 따라서 srcsetx값을 w값으로 변경하여 DPR 값이 아닌 화면의 너비(width) 값으로 분기가 가능하도록 조치한다.

해결

DPR에서 Width로 기준을 바꾸자

resourceJoinWithWidth(pathList) {
  return pathList.map(x => `${x.path} ${x.w}w`).join(', ')
},

너비를 기준으로 srcset 속성 값으로 들어갈 문자열을 만드는 함수를 새로 작성하고, img 태그의 srcset 속성에 이를 넣는다.

<img v-else
  class="main_graphic img-500"
  :src="resource('web/main_graphic@1x.png')"
  :srcset="resourceJoin([
	{path: resource('web/main_graphic@1x.png'), w: 768}, 
	{path: resource('web/main_graphic@2x.png'), w: 1000}
  ])"
>

이제 브라우저 너비가 768px 이하라면 1x 해상도의 이미지가, 그 이상이면 2x 해상도의 이미지가 출력될 것이다.

제대로 받아오는 것을 볼 수 있다!

참고자료

HTML IMG의 srcset과 sizes 속성

0개의 댓글