(번역) 웹 성능을 개선하기 위한 세 가지 큰 수단에 대한 레이아웃 위치의 영향
우리는 Shopify 판매자들의 웹 성능을 개선하려고 노력하고 있으며, 레이아웃 위치와 관련된 세 가지 일반적인 문제가 있다는 것을 알게 되었습니다.
- 스크롤되지 않은 부분에 있는 이미지의 지연 로딩
- 스크롤되지 않은 부분에 있는 요소에 필요한 CSS 비동기 로딩
- Largest Contentful Paint(LCP) 이미지의 페칭 우선순위를 지정하지 않음
과거에 이 세 가지 문제는 Shopify 테마에서 해결하기 어려웠기 때문에, 우리는 이를 해결하는 데 도움이 되는 새로운 기능을 개발 중입니다. 이 글은 이러한 성능 안티패턴을 더 잘 이해하는 데 도움이 되며, 이후에 새로운 기능이 출시되는 즉시 사용하는 데 도움이 될 것입니다.
LCP나 코어 웹 바이탈에 익숙하지 않나요? 2023 주요 웹 성능 지표에 대해 알아보세요.
이미지 지연 로딩은 뭘까요?
이미지 지연 로딩은 필요하지 않은 파일의 불필요한 다운로드를 방지하는 웹 성능 패턴입니다. 이미지 지연 로딩을 사용하면 다운로드되는 총 바이트 수를 줄일 수 있습니다. 또한, 브라우저가 초기 렌더링에 필요한 중요한 에셋을 더 우선순위가 높도록 지정할 수 있게 합니다. “지연” 로딩의 반대는 “즉시” 로딩입니다. 즉시 로딩은 브라우저에서 <img>
태그를 사용할 때의 기본 동작입니다.
이미지의 경우, 두 가지 이미지 지연 로딩 방식이 존재합니다.
<img src="image.jpg" loading="lazy" />
- lazysizes같은 라이브러리 사용하기
<script src="lazysizes.min.js" async=""></script>
<img data-src="image.jpg" class="lazyload" />
두 방법 모두 IntersectionObserver를 사용하여 이미지가 뷰포트와 “교차(intersecting)”하기 직전에 이미지를 로드할 수 있도록 관찰합니다. 이는 “스크롤되지 않은 부분”에 있거나 화면에서 숨겨진 이미지에 유용합니다. 문제는 이미지가 로드 시점에 뷰포트에 있을 때 발생합니다. 브라우저는 레이아웃과 페인트를 수행한 후에야 파일을 로드합니다. 그런 다음 자바스크립트가 교차점을 계산할 수 있습니다. 이는 기본 옵션인 즉시 로딩에 비해 상당한 지연이 발생합니다.
만약 당신이 지연 로딩을 사용하기로 했다면, 어떤 방법이 가장 좋은지 스스로 질문해 봐야 합니다. 네이티브 지연 로딩을 사용하면 추가 자바스크립트 종속성이 필요하지 않다는 장점이 있습니다. 코어 웹 바이탈 지표인 Interaction to Next Paint(INP)가 곧 출시될 예정이므로, 자바스크립트 종속성을 가능한 한 간결하게 유지하고 싶을 겁니다.
lazysizes와 같은 라이브러리가 네이티브에 비해 갖는 주요 이점은 지연 로드된 이미지의 sizes
속성을 자동으로 계산할 수 있다는 것입니다. 좋은 소식은 이것이 HTML 사양의 새로운 기능으로 제안되고 승인되었다는 점입니다. 크롬은 곧 개발자 시험판을 출시할 예정이며, 작성 당시 파이어폭스와 사파리는 출시에 대한 긍정적인 반응을 보이고 있습니다.
안타깝게도, 두 가지 방법 모두 즉시 로딩이 필요한 모든 이미지에 대해 sizes
속성을 수동으로 설정해야 합니다. 이미지의 크기는 페인트가 완료된 후에야 계산할 수 있으므로 위에서 언급한 지연과 동일한 문제가 발생합니다. sizes
속성은 올바르게 설정하기 어렵기로 악명 높습니다. 이 경우 Responsive Image Linter 크롬 익스텐션이 더 정확하게 조정하는 데 도움이 될 수 있습니다. sizes
속성에 대한 "잘못된" 값을 입력하면 올바른 값을 제안합니다. srcset
제안은 다소 공격적인 방법일 수 있습니다. 완벽하게 크기가 조정된 이미지가 모든 너비에 필요하지 않습니다. 캐싱을 활용하려면 너비 목록을 짧게 만드는 것이 더 좋습니다.
Shopify를 사용해 반응형 이미지를 만드는 방법을 자세히 알고 싶나요? 글과 데모, Shopify with Liquid의 반응형 이미지를 확인하세요.
잘못된 지연 로딩으로 인한 성능 영향
과거에는 테마 개발자가 섹션 순서나 위치를 결정할 수 없었습니다. 이로 인해 많은 테마가 모든 이미지에 지연 로딩을 적용했습니다. 그 결과 82%의 Shopify 사이트가 이제 Largest Contentful Paint(LCP) 이미지를 지연 로딩합니다.
이 성능 안티 패턴은 기기와 네트워크 속도에 따라 LCP가 다르게 지연되는 원인이 됩니다. 작년에 Dawn 테마를 개선하기 위한 테스트에서 기사 헤더 이미지를 지연 로딩에서 즉시 로딩으로 전환하여 성능이 0.5초 향상되었습니다.
또한, HTTPArchive를 통해 웹 전체를 살펴보면, LCP 요소를 지연 로딩하는 Shopify 사이트의 분포는 즉시 로딩하는 분포보다 느립니다. 지연 로딩된 사이트의 LCP 중간값은 즉시 로딩된 사이트보다 1초 느립니다. 75 백분위수(percentile)에서는 그 차이가 1.4초로 증가합니다.
비동기 CSS 로딩은 뭘까요?
Shopify 테마는 종종 테마의 유연성을 위해 더 많은 CSS를 필요로 합니다. 이를 고려할 때, CSS 비동기 로딩은 유용한 패턴입니다. 이는 바로 필요하지 않을 수 있는 CSS의 우선순위를 낮추는 데 도움이 됩니다. 이 패턴은 기능이라기보다는 해킹에 가깝지만 유용합니다. 코드는 일반적으로 이렇게 작성합니다.
<link rel="stylesheet" href="my.css" media="print" onload="this.media='all'" />
<noscript>
<link rel="stylesheet" href="my.css" />
</noscript>
어떻게 작동할까요? 브라우저는 미디어 속성이 “screen” 또는 “all”인 스타일 시트에만 다운로드 우선순위를 가장 높게 설정합니다. 미디어가 “print”로 설정되면 우선순위를 “lowest”로 설정합니다. 이때 다른 우선순위가 더 높은 파일이 먼저 다운로드되며 초기 렌더링은 print 스타일을 사용하지 않습니다. 그런 다음 load 이벤트가 발생한 후 자바스크립트는 미디어를 “all” 또는 “screen”으로 변경합니다. 이 시점에 CSS 파일 우선순위는 “highest”로 변경되고 스타일이 도착하면 페이지가 다시 렌더링 됩니다. 자바스크립트가 비활성화된 브라우저를 위해 폴백으로, 표준 <link>
의 폴백을 <noscript>
태그 안에 추가하는 것이 좋습니다.
아래 렌더링 다이어그램에 그 과정이 어떻게 진행되는지 나타내 보겠습니다.
잘못된 비동기 CSS 로딩으로 인한 성능 영향
우리는 많은 Shopify 사이트가 이 패턴으로 인해 레이아웃 시프트가 발생하는 것을 확인했습니다. 이는 또 다른 핵심 웹 지표인 Cumulative Layout Shift(CLS)에 영향을 미칩니다. 다시 말해, 스크롤되지 않은 부분의 요소에 해당 스타일이 필요하면 사용자는 미처 스타일이 적용되지 않은 콘텐츠(FOUC)를 보게 됩니다. 저는 이를 “FOSSC”(Flash of Semi-styled Content)라고 부릅니다.
늦게 로딩된 CSS로 인한 스타일 리플로우는 완벽하게 만들어진 이미지 지연 로딩 전략을 파괴할 수도 있습니다. IntersectionObserver는 이미지가 뷰포트 안에 있다고 “잘못” 감지할 수 있습니다. 하지만 늦게 로딩된 CSS가 그 이미지를 페이지 아래로 밀어낼 수 있습니다. 삽입된 컴포넌트도 이 문제를 일으킬 수 있습니다. 결과적으로, 최종 페이지 레이아웃에서 몇 개만 다운로드해야 하는 경우에도 많은 이미지가 한 번에 로드될 수 있습니다.
페칭 우선순위는 뭘까요?
페칭 우선순위는 더 미묘한 개념입니다. 웹사이트를 표시하는 데 필요한 파일의 수가 많기 때문에, 다운로드 대기열에 있는 파일을 더 스마트하게 정렬하면 더 빠른 사용자 경험을 가져올 수 있습니다. 브라우저는 서로 다른 파일 형식과 컨텍스트에 따라 다운로드 우선순위를 설정합니다. 예를 들어, 브라우저는 CSS 파일을 가장 높은 우선순위로 가져옵니다. CSS 파일은 렌더링을 차단하고 초기 렌더링에 필요하기 때문입니다.
이미지는 기본적으로 낮은 우선순위로 가져옵니다. 브라우저가 오프스크린 이미지보다 더 빨리 표시해야하는 이미지가 있다는 것을 것을 판단하면, 페칭 우선순위를 높음으로 변경할 수 있습니다.
Fetch Priority API는 브라우저에 특정 에셋에 대해 다른 우선순위를 설정하기를 원한다는 의미를 제공합니다. 예를 들어, 특정 이미지 요소가 LCP 요소가 될 것이 확실하다면 우선순위를 높음으로 설정할 수 있습니다.
<img src="LCPimage.jpg" fetchpriority="high" alt="Important image!" />
반면에, 특정 에셋이 중요하지 않다고 알고 있다면 다른 더 중요한 파일이 더 빨리 다운로드될 수 있도록 우선순위를 낮게 설정할 수 있습니다.
이 기능을 너무 많이 사용하지 마세요! 모든 것이 중요하다면 아무것도 중요하지 않으며, 우리는 브라우저를 기본 동작보다 덜 최적화된 상태로 만들었다는 점을 알아야 합니다.
잘못된 페칭 우선순위로 인한 성능 영향
대부분의 경우, LCP 이미지를 처음부터 높은 우선순위로 가져오지 못하면 페이지 로딩 시간이 (보수적으로) 약 0.3초 더 느려질 수 있습니다.
하지만, fetchpriority="high"
로 너무 많은 에셋을 설정하면 우선순위를 잘못 사용하게 될 수 있습니다. 로딩 우선순위가 비최적화되어 FCP와 LCP가 지연될 수 있습니다.
해결책이 곧 등장합니다.
곧 Liquid에 이러한 문제에 더 잘 대처할 수 있는 새로운 기능이 출시될 예정입니다. 조만간 있을 발표를 기다려 주세요.
그전에는 제품 및 아티클 페이지와 같은 안정적인 페이지에 대해 더 스마트한 기본값을 사용하고 나머지에는 섹션 설정을 사용할 수 있습니다. 자세한 내용과 예는 Shopify에서 이미지 최적화하기를 참조하세요.