목차
Image Lazy Loading이란?
이미지 지연 로딩은 페이지 안에 있는 이미지들이 실제로 화면에 보여질 필요가 있을 때 로딩할 수 있도록 하는 기법이다.
웹 페이지 내에서 바로 이미지를 로딩하지 않고 로딩 시점을 뒤로 미루는 것이라 볼 수 있다.
Image Lazy Loading을 이용하면 다음과 같은 두 가지 이점이 있다.
1. 성능 향상
페이지 초기 로딩 시 필요한 이미지의 수를 줄일 수 있다.
리소스 요청을 줄이는 것은 다운로드 bytes를 줄이는 것이며, 이는 유저가 사용할 수 있는 제한된 네트워크 대역폭의 경쟁을 줄이는 것을 의미한다.
디바이스가 다른 리소스들을 더 빨리 처리해서 다운로드하도록 하여 페이지를 훨씬 빨리 유저가 이용할 수 있도록 한다.
2. 비용 감소
통신 비용 관점에 있다.
lazy-loading은 이미지가 보여지지 않으면 절대 로딩하지 않으므로, 페이지 내에서 전달할 총 바이트 용량을 줄일 수 있다.
특히 페이지 이탈하거나 페이지 제일 상단에만 서비스를 이용하는 유저들에게 효과적이다.
그래서 네트워크로부터 전송될 바이트 감소는 전송 비용을 줄인다.
💡 <img> 태그를 이용한 방법
먼저, 이미지 로딩을 사전에 막는 방법이 있다.
일반적으로 <img> 태그를 이용해서 이미지를 로드하기 위해, 브라우저는 태그 내 src 속성을 이용한다.
만약 브라우저가 src 속성을 가지면, 이미지를 무조건 로드한다.
이미지들의 로딩을 지연시키려면, src 속성 대신 data-src 속성에 이미지 URL을 지정하면 된다. 그러면 src는 비워져 있고, 브라우저는 해당 이미지를 로드하지 않게 된다.
위 방식으로 이미지 로드를 사전에 막고, 브라우저에게 해당 이미지를 언제 로딩할 것인지 알려주어 그때에 src 속성에 URL을 지정해준다.
상단 처음 이미지 같은 경우, 미리 로딩되도록 src 속성으로 URL 적용해두는 것이 UX 상 좋다. 페이지 상단에 있는 이미지들은 JS 파일이 로딩되고 이벤트가 발생할 때까지 기다릴 수 없으므로, 가능한 빨리 보여지게 하는 것이 좋다.
Intersection Obsever API
Intersection Observer API는 타겟 요소와 상위 요소 또는 최상위 document 의 viewport 사이의 intersection 내의 변화를 비동기적으로 관찰하는 방법입니다.
(https://developer.mozilla.org/ko/docs/Web/API/Intersection_Observer_API)
쉼게 말해, 이 API는 타겟 요소가 뷰포트에 들어가는 것을 감지하고 액션을 취하는 것을 간단하게 만들어 주는 것이다.
💡 intersection 정보가 필요한 이유
- 페이지가 스크롤 시 이미지 Lazy-loading(지연 로딩)할 때 사용
- Infinite scrolling을 통해 스크롤 시 새로운 콘텐츠를 불러올 때 사용
- 광고 수익을 계산하기 위한 용도로 광고의 가시성 보고
- 사용자에게 결과가 표시되는 여부에 따라 작업이나 애니메이션을 수행할 지 여부 결정
기존에 특정 지점을 관찰하기 위해서는 getBoundingClientRect() 함수를 사용했다. 이 함수는 브라우저가 웹 페이지 일부 또는 전체를 다시 그려야 하는 reflow 현상이 발생한다는 단점이 있다.
하지만, Intersection Observer API를 이용하면 위의 문제를 해결할 수 있다.
비동기적으로 실행되기 때문에 메인 스레드에 영향을 주지 않으면서 변경 사항을 관찰할 수 있으며, IntersectionObserverEntry 속성을 활용하면 getBoundingClientRect()를 호출한 것과 같은 결과를 알 수 있어 따로 getBoundingClientRect() 함수를 호출할 필요가 없어 리플로우 현상도 방지할 수 있다.
Intersection Observer API를 이용한 이미지 Lazy Load 구현
이미지 로드를 지연시키기 위해 모든 이미지에 옵저버를 부착시킨다.
엘리먼트가 뷰포트에 들어간 것을 API가 감지했을 때, isIntersecting 속성을 이용해 URL을 data-src 속성에서 src 속성으로 이동시켜 브라우저가 이미지를 로드하도록 트리거를 일으키면 된다.
예시를 한 번 보도록 하자.
const options = {
root: null,
rootMargin: "0px 0px 30px 0px",
threshold: 0,
};
// IntersectionObserver 를 등록한다.
const io = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
// 관찰 대상이 viewport 안에 들어온 경우 image 로드
if (entry.isIntersecting) {
console.log(`entry: ${entry}`);
// data-set 정보를 타겟의 src 속성에 설정
entry.target.src = entry.target.dataset.src;
// image 클래스 제거
entry.target.classList.remove("image");
// 이미지를 불러왔다면 타겟 엘리먼트에 대한 관찰을 멈춘다.
observer.unobserve(entry.target);
}
});
}, options);
// 관찰할 대상을 선언하고, 해당 속성을 관찰시킨다.
const images = document.querySelectorAll(".image");
images.forEach((el) => {
io.observe(el);
});
entries는 IntersectionObserverEntry 인스턴스의 배열이다. 아래의 속성들을 포함하고 있다.
options
- root : 타겟의 가시성을 검사하기 이해 뷰포트 대신 사용할 요소 객체(루트 요소)를 지정한다. 지정하지 않거나 null인 경우 브라우저의 뷰포트가 된다.
- rootMargin: margin을 이용해 Root 범위를 확장하거나 축소할 수 있다.
- threshold: 타겟의 가시성이 얼마나 필요한지 백분율로 표시하는 것이다.
- 이해가기 쉬운 이미지를 퍼왔다. (이미지 출처: https://heropy.blog/2019/10/27/intersection-observer/)
❗ 하지만, Intersection Observer API는 아직 모든 브라우저가 지원되고 있지는 않다.
그래도 IE를 제외하곤 거의 모든 브라우저에서 지원되고 있어(전체 브라우저에서 약 96%가 지원 하고 있음), 해당 API가 지원되지 않는 브라우저에서는 event listener 방식으로 사용하도록 하는 방법이 있다.
실무 적용 및 성능 측정 결과
✨ 실무에 적용해 보기 ✨
실제로 이미지를 많이 사용하고 있고 스크롤 이벤트 처리가 많은 실무 웹 페이지에 스크롤 이벤트 최적화와 함께 적용한 후,
Google lighthouse를 이용하여 성능 측정해 본 결과는 아래와 같다.
브라우저 테스트 환경
- Chrome 개발자도구의 Lighthouse를 통해 성능 측정
- 브라우저 캐시 비활성화 (크롬 개발자 도구의 Network 탭 > Disable cache 설정)
- 제한된 네트워크 설정 (크롬 개발자 도구의 Network 탭 > Disable cache 설정 우측에 있는 Throttling 옵션 선택하여 느린 인터넷 환경 속도로 설정 - Fast 3G 선택)
- 쿠키 및 사이트 데이터 사용하지 않기 위해 Chrome Secret Mode로 접속 후 테스트
Idle은 웹 페이지가 최소한으로 상호 작용할 수 있는 상태가 될 때까지 걸리는 시간인데, 확실히 줄어든 것을 볼 수 있다.
🎇 성능 측정 결과
- FCP
- 페이지 로드가 시작된 후 뷰포트 내 의미있는 콘텐츠 일부가 처음 화면에 렌더링 될 때까지의 시간
- 2.0s → 1.1s
- Speed Index
- 뷰포트 내 콘텐츠가 눈에 띄게 채워지는 속도. Lighthouse 기준 3.4초 이내로 들면 빠른 편이라고 한다. (참고)
- 4.2s → 2.8s
- LCP
- 페이지에서 가장 용량이 큰 컨텐츠가 표시되는 시점으로, LCP를 기준으로 사용자 중심의 페이지 로드 속도를 판단한다.
- 15.3s → 6.9s (15초 대에서 6초 대로 줄다니..😲 하지만 Lighthouse 기준 LCP는 4초내로 들어야 양호한 편이며, 2.5초 내로 들어야 빠른 편으로 본다고 한다. 아직 갈 길이 멀었다.. )
- 두 배 이상 빨라졌지만, 이미지 파일에 대한 최적화가 더 필요해 보인다. 더 고민해 보아야 겠다.
- TTI
- 페이지가 완전히 사용자와 상호 작용할 수 있는 상태가 되는데 걸리는 시간
- 2.0s → 1.1s
- CLS
- 사용자가 예상하지 못한 레이아웃을 경험하는 빈도를 정량화해서 시각적인 안정성을 판단하는 기준
- 0.933 → 0.091
이미지 지연 로딩과 스크롤 이벤트 최적화를 통해 웹 페이지 성능이 확실히 개선된 것을 볼 수 있다.
더 개선할 수 있는 방법을 찾아 적용해 보아야 겠다.
참고
https://helloinyong.tistory.com/297
웹 성능 최적화를 위한 Image Lazy Loading 기법
현재 화면에 보여지지 않는 lazy loading된 이미지들은 웹 페이지 초기의 로딩 시간을 단축하여 웹 성능을 향상시킵니다. 이 글은 lazy loading 처리 기법과 관련된 모든 것들을 깊게 다루게 됩니다. 해
helloinyong.tistory.com
http://blog.hyeyoonjung.com/2019/01/09/intersectionobserver-tutorial/
Intersection Observer API의 사용법과 활용방법 · Yoon's devlog
Intersection Observer API의 사용법과 활용방법 Web API 중 하나인 Intersection Observer API를 알아보고 어떻게 활용할 수 있는지에 대해 정리한 글입니다. Intersection Observer API(교차 관찰자 API)를 들어본 적이
blog.hyeyoonjung.com
https://heropy.blog/2019/10/27/intersection-observer/
Intersection Observer - 요소의 가시성 관찰
Intersection observer는 기본적으로 브라우저 뷰포트(Viewport)와 설정한 요소(Element)의 교차점을 관찰하며, 요소가 뷰포트에 포함되는지 포함되지 않는지, 더 쉽게는 사용자 화면에 지금 보이는 요소인
heropy.blog
https://fe-developers.kakaoent.com/2022/220120-ux-and-perf-in-kakaowebtoon/
'Devlog > JavaScript' 카테고리의 다른 글
[JavaScript] $(document).ready() / $(function(){}) / window.onload 차이 (0) | 2022.08.04 |
---|---|
[JavaScript] script의 속성 async와 defer (0) | 2022.07.12 |
[JavaScript] Promise 알아보기 (0) | 2022.06.29 |
[JavaScript] Slick Slider 모바일 스크롤 시 autoplay 멈춤 현상 (0) | 2022.06.09 |
[JavaScript/CSS] Slick Slider 오류 (0) | 2022.06.02 |