설명 구문

이 모듈에서는 브라우저에서 표시할 이미지에 대해 최상의 결정을 내릴 수 있도록 이미지 선택권을 제공하는 방법을 알아봅니다. srcset는 특정 중단점에서 이미지 소스를 바꾸는 메서드가 아니며 한 이미지를 다른 이미지로 교체하기 위한 것이 아닙니다. 이러한 구문을 사용하면 브라우저에서 Google과는 별개로 매우 까다로운 문제를 해결할 수 있습니다. 즉, 표시 영역 크기, 디스플레이 밀도, 사용자 환경설정, 대역폭 및 기타 수많은 요소를 비롯한 사용자의 탐색 컨텍스트에 맞는 이미지 소스를 원활하게 요청하고 렌더링할 수 있습니다.

이는 매우 중요한 과제입니다. 단순히 웹에 사용할 이미지를 마크업하고 이 작업을 잘 수행하려면 우리가 액세스할 수 있는 것보다 더 많은 정보를 포함한다면 우리가 고려해야 할 것보다 더 많다는 것입니다.

x로 밀도 설명

너비가 고정된 <img>는 사용자 디스플레이의 밀도(화면을 만드는 실제 픽셀 수)와 관계없이 모든 탐색 컨텍스트에서 동일한 표시 영역을 차지합니다. 예를 들어 고유 너비가 400px인 이미지는 원래 Google Pixel과 최신 Pixel 6 Pro 모두에서 브라우저 표시 영역 거의 전체를 차지합니다. 두 기기 모두 정규화된 412px 논리 픽셀 너비 표시 영역을 갖습니다.

Pixel 6 Pro의 디스플레이는 훨씬 선명합니다. 6 Pro의 실제 해상도는 1440 × 3120픽셀인 반면 Pixel은 1080 × 1920픽셀(화면 자체를 구성하는 하드웨어 픽셀 수)입니다.

기기의 논리 픽셀과 실제 픽셀 간의 비율은 해당 디스플레이의 기기 픽셀 비율 (DPR)입니다. DPR은 기기의 실제 화면 해상도를 표시 영역의 CSS 픽셀로 나누어 계산합니다.

콘솔 창에 표시된 DPR 2

따라서 기존 Pixel의 DPR은 2.6이고 Pixel 6 Pro의 DPR은 3.5입니다.

DPR이 1보다 큰 첫 번째 기기인 iPhone 4는 기기 픽셀 비율을 2로 보고합니다. 즉, 화면의 물리적 해상도는 논리 해상도의 두 배입니다. iPhone 4 이전의 모든 기기의 DPR은 1: 1 논리 픽셀과 물리적 픽셀 1입니다.

DPR이 2인 디스플레이에서 400px 너비의 이미지를 보면 각 논리 픽셀은 디스플레이의 물리적 픽셀 4개(가로 2개와 세로 2개)에서 렌더링되고 있는 것입니다. 이 이미지는 고밀도 디스플레이를 활용할 수 없습니다. DPR이 1인 디스플레이에서와 동일하게 보입니다. 물론 브라우저의 렌더링 엔진에서 '그리는' 모든 항목(예: 텍스트, CSS 도형, SVG)은 고밀도 디스플레이에 맞게 그려집니다. 하지만 이미지 형식 및 압축에서 알아본 것처럼 래스터 이미지는 픽셀의 고정된 그리드입니다. 항상 눈에 띄게 명확하지는 않지만, 고밀도 디스플레이에 맞게 확대된 래스터 이미지는 주변 페이지에 비해 낮은 해상도로 표시됩니다.

이러한 확대를 방지하려면 렌더링되는 이미지의 고유 너비가 800픽셀 이상이어야 합니다. 너비가 400인 논리 픽셀 레이아웃의 공간에 맞게 축소되면 800픽셀 이미지 소스의 픽셀 밀도는 두 배가 되며 DPR이 2인 디스플레이에서 멋지고 선명하게 보입니다.

밀도의 차이를 보여주는 꽃잎 클로즈업

DPR이 1인 디스플레이는 이미지의 증가된 밀도를 사용할 수 없으므로 디스플레이에 맞게 축소됩니다. 알다시피 다운스케일된 이미지는 보기에도 좋습니다. 저밀도 디스플레이에서 고밀도 디스플레이에 적합한 이미지는 다른 저밀도 이미지와 비슷하게 보입니다.

이미지 및 성능에서 알아본 것처럼, 400px로 축소된 이미지 소스를 보는 저밀도 디스플레이를 사용하는 사용자는 고유한 너비가 400px인 소스만 필요합니다. 훨씬 큰 이미지는 시각적으로 모든 사용자에게 적합하지만, 작은 저밀도 디스플레이에서 렌더링된 거대한 고해상도 이미지 소스는 다른 작은 저밀도 이미지와 보이지만 훨씬 더 느립니다.

짐작할 수 있듯이 DPR이 1인 휴대기기거의 드물게 발생하지만 '데스크톱' 탐색 컨텍스트에서는 여전히 일반적입니다. 맷 홉스가 공유한 데이터에 따르면 2022년 11월부터 발생한 GOV.UK 탐색 세션의 약 18% 는 북한이 1인 것으로 나타났습니다. 고밀도 이미지는 이러한 사용자가 예상하는 방식으로 보이지만 훨씬 더 높은 대역폭과 처리 비용을 발생시키며, 특히 저밀도 디스플레이를 사용할 가능성이 높은 구형의 덜 강력한 기기를 사용하는 사용자에게 특히 우려됩니다.

srcset를 사용하면 저해상도 디스플레이를 사용하는 사용자에게 동일한 대역폭 비용을 전달하지 않으면서 고해상도 디스플레이를 사용하는 기기만 선명하게 보일 만큼 큰 이미지 소스를 수신할 수 있습니다.

srcset 속성은 이미지를 렌더링하는 하나 이상의 쉼표로 구분된 후보를 식별합니다. 각 후보는 src에서 사용하는 URL과 이미지 소스를 설명하는 문법, 두 가지로 구성됩니다. srcset의 각 후보는 고유한 너비 ('w 문법') 또는 의도한 밀도 ('x 구문')로 설명됩니다.

x 구문은 '이 소스는 이 밀도의 디스플레이에 적합합니다'의 약식 표현입니다. 후보 뒤에 2x가 오는 것은 DPR이 2인 디스플레이에 적합합니다.

<img src="low-density.jpg" srcset="double-density.jpg 2x" alt="...">

srcset를 지원하는 브라우저에는 두 가지 후보가 표시됩니다. 2x은 DPR이 2인 디스플레이에 적합하다고 설명하는 double-density.jpgsrc 속성의 low-density.jpg(srcset에서 더 이상 적절한 항목이 없는 경우 선택된 후보)입니다. srcset를 지원하지 않는 브라우저의 경우 속성과 그 콘텐츠가 무시됩니다. src의 콘텐츠가 평소와 같이 요청됩니다.

srcset 속성에 지정된 값을 지침으로 오해하기 쉽습니다. 이 2x는 연결된 소스 파일이 DPR이 2(소스 자체에 관한 정보)인 디스플레이에서 사용하기에 적합하다고 브라우저에 알립니다. 소스를 사용하는 방법을 브라우저에 알려주지 않고 소스를 사용할 수 있는 방법을 브라우저에 알려줄 뿐입니다. 이는 미묘하지만 중요한 차이점입니다. 이는 배밀도 디스플레이에 사용하기 위한 이미지가 아니라 이중 밀도 이미지입니다.

'이 소스는 2x 디스플레이에 적합합니다'라는 구문과 '2x 디스플레이에서 이 소스를 사용'이라는 구문의 차이는 인쇄물에서 미미합니다. 하지만 디스플레이 밀도는 브라우저가 렌더링할 후보를 결정하는 데 사용하는 수많은 상호 연결 요소 중 하나일 뿐이며 이 중 일부만 알 수 있습니다. 예를 들어 개별적으로 사용자가 prefers-reduced-data 미디어 쿼리를 통해 대역폭 절약 브라우저 환경설정을 사용 설정했는지 확인하고 이를 사용하여 디스플레이 밀도와 관계없이 항상 사용자를 저밀도 이미지를 사용하도록 선택할 수 있습니다. 하지만 모든 개발자가 모든 웹사이트에서 일관되게 구현하지 않으면 사용자에게 별다른 도움이 되지 않습니다. 한 사이트에서는 선호도를 존중했지만 다음 사이트에서는 대역폭을 이해하기 어려운 이미지 벽에 부딪힐 수 있습니다.

srcset/sizes에서 사용하는 의도적으로 모호한 리소스 선택 알고리즘은 브라우저가 대역폭 하락이 있거나 데이터 사용량 최소화 환경설정에 따라 저밀도 이미지를 선택할 수 있는 여지를 남겨두었습니다. 이때 Google은 방법, 시기 또는 기준에 대한 책임을 지지 않습니다. 브라우저가 더 잘 처리할 수 있는 책임과 추가 작업을 맡는 것은 무의미합니다.

w로 너비 설명

srcset는 이미지 소스 후보에 관한 두 번째 유형의 설명자를 허용합니다. 이것이 훨씬 더 강력하며 우리의 목적상 훨씬 이해하기 쉽습니다. 특정 디스플레이 밀도에 적합한 크기를 가진 후보로 플래그를 지정하는 대신 w 구문은 각 후보 소스의 고유한 너비를 설명합니다. 다시 말하지만, 각 후보는 콘텐츠, 자르기, 가로세로 비율이 동일하며 크기가 동일하게 저장됩니다. 하지만 이 경우에는 사용자의 브라우저가 두 가지 후보 중에서 선택해야 합니다. small.jpg는 고유 너비가 600px이고, large.jpg는 고유 너비가 1,200px 소스입니다.

srcset="small.jpg 600w, large.jpg 1200w"

이 정보는 브라우저에 이 정보로 수행해야 하는 작업을 알려주지는 않으며 이미지를 표시할 후보 목록을 제공하기만 합니다. 브라우저에서 렌더링할 소스를 결정하기 전에 추가 정보(이미지가 페이지에서 렌더링되는 방식에 관한 설명)를 제공해야 합니다. 이렇게 하려면 sizes 속성을 사용합니다.

sizes로 사용 설명

브라우저는 이미지 전송에 있어 매우 뛰어난 성능을 발휘합니다. 이미지 애셋 요청은 스타일시트 또는 JavaScript를 요청하기 훨씬 전에 시작되며, 마크업이 완전히 파싱되기 전에 시작되는 경우가 많습니다. 브라우저가 이러한 요청을 하면 마크업 외에 페이지 자체에 관한 정보가 없습니다. 외부 스타일시트에 대한 요청을 아직 시작하지 않았을 수 있습니다. 브라우저가 마크업을 파싱하고 외부 요청을 시작하는 시점에는 사용자 표시 영역의 크기, 사용자 디스플레이의 픽셀 밀도, 사용자 환경설정 등 브라우저 수준의 정보만 포함됩니다.

이는 이미지가 페이지 레이아웃에서 어떻게 렌더링되는지 알려주지는 않습니다. 가로로 스크롤되는 컨테이너를 차지할 수 있으므로 표시 영역을 img 크기의 상한값의 프록시로 사용할 수도 없습니다. 따라서 브라우저에 이 정보를 제공하고 마크업을 사용하여 정보를 제공해야 합니다. 이러한 요청에는 위 코드만 사용할 수 있습니다.

srcset와 마찬가지로 sizes는 마크업이 파싱되는 즉시 이미지에 관한 정보를 사용할 수 있도록 하기 위한 것입니다. srcset 속성이 '소스 파일과 고유 크기'의 줄임말인 것처럼 sizes 속성은 '여기가 레이아웃에서 렌더링된 이미지의 크기입니다'의 줄임 표기입니다. 이미지를 설명하는 방식은 표시 영역을 기준으로 합니다. 다시 말하지만, 이미지 요청 시 브라우저에서 보유하는 레이아웃 정보는 표시 영역 크기뿐입니다.

인쇄물에서는 다소 복잡하게 들릴 수 있지만 실제로는 훨씬 더 이해하기 쉽습니다.

<img
 sizes="80vw"
 srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
 src="fallback.jpg"
 alt="...">

여기서 이 sizes 값은 img가 차지하는 레이아웃에서 공간의 너비가 표시 영역의 80% 인 80vw임을 브라우저에 알립니다. 이는 안내가 아니라 페이지 레이아웃에서의 이미지 크기에 관한 설명입니다. '이 이미지가 표시 영역의 80% 를 차지하도록 합니다'라고 표시되어 있지 않지만 '페이지가 렌더링되면 이 이미지가 표시 영역의 80% 를 차지합니다.'라고 표시되어 있지 않습니다.

개발자로서 해야 할 일은 끝났습니다. 후보 소스 목록을 srcset로 정확하게 설명했고 이미지 너비를 sizes로 정확하게 설명했습니다. srcsetx 구문과 마찬가지로 나머지는 브라우저에서 결정합니다.

그러나 이 정보가 사용되는 방식을 완전히 이해하기 위해 사용자의 브라우저가 이 마크업을 마주했을 때 내린 결정을 살펴보겠습니다.

이 이미지가 사용 가능한 표시 영역의 80% 를 차지한다고 브라우저에 알렸습니다. 따라서 너비가 1,000픽셀인 표시 영역이 있는 기기에서 이 img를 렌더링하면 이 이미지는 800픽셀을 차지하게 됩니다. 그러면 브라우저는 이 값을 가져와 srcset에서 지정한 각 이미지 소스 후보의 너비로 나눕니다. 가장 작은 소스의 고유 크기는 600픽셀이므로 600÷800=0.75가 됩니다. 중간 이미지의 너비는 1200픽셀, 즉 1200÷800=1.5입니다. 가장 큰 이미지는 너비가 2,000픽셀(2,000÷800=2.5)입니다.

이러한 계산의 결과 (.75, 1.52.5)는 사실상 사용자의 표시 영역 크기에 맞게 특별히 조정된 DPR 옵션입니다. 브라우저는 사용자의 화면 표시 밀도에 관한 정보도 가지고 있으므로 다음과 같이 일련의 결정을 내립니다.

이 표시 영역 크기에서 small.jpg 후보는 사용자의 화면 밀도와 관계없이 삭제됩니다. 계산된 DPR이 1보다 낮으므로 이 소스는 모든 사용자에 대해 확장이 필요할 수 있으므로 적절하지 않습니다. DPR이 1인 기기에서 medium.jpg가 가장 근접한 일치 항목을 제공합니다. 이 소스는 1.5의 DPR에서 표시하기에 적합하므로 필요한 것보다 약간 크지만 축소는 시각적으로 매끄러운 프로세스입니다. DPR이 2인 기기에서는 large.jpg가 가장 근접하므로 선택됩니다.

동일한 이미지가 600픽셀 너비의 표시 영역에서 렌더링되는 경우 계산 결과는 완전히 달라집니다. 80vw는 이제 480픽셀입니다. 소스의 너비를 이에 따라 나누면 1.25, 2.5, 4.1666666667를 얻습니다. 이 표시 영역 크기에서는 small.jpg가 1x 기기에서 선택되고 medium.jpg는 2x 기기에서 일치합니다.

이 이미지는 이러한 모든 탐색 컨텍스트에서 동일하게 보입니다. 모든 소스 파일은 크기 외에는 완전히 동일하며, 각 소스 파일은 사용자의 화면 밀도가 허용하는 한 선명하게 렌더링됩니다. 하지만 가장 큰 표시 영역과 가장 높은 밀도의 디스플레이를 수용하기 위해 모든 사용자에게 large.jpg를 제공하는 대신 사용자에게 항상 가장 작은 적합한 후보가 게재됩니다. 규범적인 구문이 아닌 설명 구문을 사용하면 수동으로 중단점을 설정하거나 향후 표시 영역 및 DPR을 고려할 필요가 없습니다. 브라우저에 정보를 제공하고 자동으로 답변을 결정하도록 허용하기만 하면 됩니다.

sizes 값은 표시 영역을 기준으로 하며 페이지 레이아웃과 완전히 독립적이므로 정보 표시 레이어를 추가합니다. 고정 너비 여백, 패딩 또는 페이지의 다른 요소의 영향 없이 표시 영역의 일정 비율 차지하는 이미지는 거의 없습니다. 백분율, em, px 등의 단위 조합을 사용하여 이미지의 너비를 표현해야 하는 경우가 많습니다.

다행히 여기에서 calc()를 사용할 수 있습니다. 반응형 이미지를 기본적으로 지원하는 모든 브라우저는 calc()도 지원하므로 CSS 단위를 혼합할 수 있습니다. 예를 들어 사용자 표시 영역의 전체 너비를 차지하고 양쪽의 1em 여백을 뺀 이미지를 사용합니다.

<img
    sizes="calc(100vw-2em)"
    srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1600w, x-large.jpg 2400w"
    src="fallback.jpg"
    alt="...">

중단점 설명

반응형 레이아웃으로 작업하는 데 많은 시간을 보냈다면 다음 예에서 뭔가 빠진 것을 발견했을 것입니다. 레이아웃에서 이미지가 차지하는 공간은 레이아웃의 중단점에서 변경될 가능성이 매우 높습니다. 이 경우 브라우저에 약간의 세부정보를 전달해야 합니다. sizes는 렌더링된 이미지 크기에 관한 쉼표로 구분된 조합 집합을 허용합니다. 이는 srcset가 이미지 소스에 쉼표로 구분된 조합을 허용하는 것과 같습니다. 이러한 조건은 익숙한 미디어 쿼리 문법을 사용합니다. 이 구문은 최초 일치 항목입니다. 미디어 조건이 일치하면 브라우저에서 sizes 속성의 파싱을 중지하고 지정된 값이 적용됩니다.

1,200px보다 큰 표시 영역에서 표시 영역의 80% 를 차지하고 양쪽의 패딩(em)을 빼고자 하는 이미지가 있다고 가정해 보겠습니다(작은 표시 영역에서는 표시 영역의 전체 너비를 차지함).

  <img
     sizes="(min-width: 1200px) calc(80vw - 2em), 100vw"
     srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
     src="fallback.jpg"
     alt="...">

사용자의 표시 영역이 1, 200픽셀보다 큰 경우 calc(80vw - 2em)는 레이아웃에서 이미지의 너비를 설명합니다. (min-width: 1200px) 조건이 일치하지 않는 경우 브라우저는 다음 값으로 이동합니다. 이 값에 연결된 특정 미디어 조건이 없으므로 100vw가 기본값으로 사용됩니다. max-width 미디어 쿼리를 사용하여 이 sizes 속성을 작성하려면 다음 안내를 따르세요.

  <img
     sizes="(max-width: 1200px) 100vw, calc(80vw - 2em)"
     srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
     src="fallback.jpg"
     alt="...">

'(max-width: 1200px)가 일치하나요? 그렇지 않다면 계속 진행하세요. 다음 값(calc(80vw - 2em))에는 조건 충족 조건이 없으므로 이 값이 선택됩니다.

이제 img 요소에 관한 모든 정보(잠재적 소스, 고유 너비, 사용자에게 이미지를 표시하는 방법)를 브라우저에 제공했으므로 브라우저는 퍼지 규칙 집합을 사용하여 해당 정보로 무엇을 할지 결정합니다. 모호하게 들린다면 이는 의도적으로 설계된 것입니다. HTML 사양에서 인코딩된 소스 선택 알고리즘은 소스를 선택하는 방법에 관해 명시적으로 모호합니다. 소스, 설명자, 이미지가 렌더링되는 방식이 모두 파싱되고 나면 브라우저는 원하는 작업을 자유롭게 수행할 수 있습니다. 브라우저에서 어떤 소스를 선택할지 확신할 수 없습니다.

'고해상도 디스플레이에서 이 소스를 사용'하는 문법은 예측 가능하지만 반응형 레이아웃의 이미지 관련 핵심 문제, 즉 사용자 대역폭 보존은 해결하지 못합니다. 화면의 픽셀 밀도는 인터넷 연결 속도와 관련이 있을 뿐입니다. 최첨단 노트북을 사용하지만 데이터 전송량 제한이 있는 연결을 통해 웹을 탐색하거나, 휴대전화에 테더링되거나, 흔들리는 비행기 Wi-Fi 연결을 사용하는 경우 디스플레이 품질에 관계없이 고해상도 이미지 소스를 사용 중지하는 것이 좋습니다.

최종 결정을 브라우저에 맡기면 엄격한 규정적인 구문을 사용하여 관리할 수 있는 것보다 훨씬 더 많은 성능 개선을 이룰 수 있습니다. 예를 들어 대부분의 브라우저에서 srcset 또는 sizes 구문을 사용하는 img는 사용자가 브라우저 캐시에 이미 있는 것보다 크기가 작은 소스를 요청하지 않습니다. 브라우저가 이미 가지고 있는 이미지 소스를 원활하게 축소할 수 있는데 동일하게 보이는 소스에 대해 새 요청을 하려면 무슨 의미가 있을까요? 하지만 사용자가 확대를 방지하기 위해 새 이미지가 필요한 지점까지 표시 영역을 조정하면 해당 요청이 계속 생성되어 모든 것이 예상대로 표시됩니다.

명시적인 제어가 부족하면 액면상으로는 조금 두려울 수 있지만, 동일한 콘텐츠의 소스 파일을 사용하기 때문에 브라우저의 결정과 관계없이 단일 소스 src를 사용할 때보다 사용자에게 '손상된' 환경을 제공할 가능성은 낮습니다.

sizessrcset 사용

이는 사용자, 독자, 브라우저 모두에게 많은 양의 정보가 됩니다. srcsetsizes은 모두 밀집 구문으로, 상대적으로 적은 수의 문자로 충격적인 정보를 설명합니다. 다시 말해, 좋든 나쁘든 설계상 이러한 구문을 덜 간결하고 사람이 더 쉽게 파싱할 수 있게 되면 브라우저가 파싱하기가 더 어려워질 수 있습니다. 문자열이 복잡해질수록 파서 오류 또는 브라우저 간 동작의 의도치 않은 차이가 발생할 가능성이 커집니다. 그러나 여기에는 이점이 있습니다. 컴퓨터가 더 쉽게 읽을 수 있는 구문이 컴퓨터가 더 쉽게 작성하는 구문입니다.

srcset는 자동화의 명확한 사례입니다. 프로덕션 환경에 맞게 여러 버전의 이미지를 수작업으로 만드는 대신 Gulp와 같은 작업 실행기, Webpack과 같은 번들러, Cloudinary와 같은 서드 파티 CDN 또는 선택한 CMS에 이미 내장된 기능을 사용하여 프로세스를 자동화하는 경우는 드뭅니다. 애초에 소스를 생성하기에 충분한 정보가 제공되면 시스템은 실행 가능한 srcset 속성에 소스를 작성하기에 충분한 정보를 갖게 됩니다.

sizes는 자동화하기가 조금 더 어렵습니다. 아시다시피, 시스템이 렌더링된 레이아웃에서 이미지의 크기를 계산할 수 있는 유일한 방법은 레이아웃을 렌더링하는 것입니다. 다행히 여러 개발자 도구가 표시되어 sizes 속성을 필기하는 프로세스를 추상화하여 수작업으로 일치시킬 수 없을 효율적으로 작업을 했습니다. 예를 들어 respImageLintsizes 속성을 검사하여 정확도를 높이고 개선을 위한 제안을 제공하기 위한 코드 스니펫입니다. Lazysizes 프로젝트는 레이아웃이 설정될 때까지 이미지 요청을 지연하여 효율성을 위해 어느 정도의 속도를 저하시키므로 JavaScript가 sizes 값을 생성할 수 있습니다. React 또는 Vue와 같은 완전한 클라이언트 측 렌더링 프레임워크를 사용하고 있다면 srcsetsizes 속성을 작성하거나 생성하는 여러 가지 솔루션이 있습니다. 이에 관한 내용은 CMS 및 프레임워크에서 자세히 설명합니다.