0%

lazy-loading

Image lazy loading 기법

lazy Loading 기법

사용자가 웹 페이지를 열면 전체 페이지의 내용이 다운로드 되어 단일 이동으로 렌더링 됩니다. 이를 통해 브라우저를 캐시할 수 있지만 사용자가 다운로드한 모든 컨텐츠를 실제로 볼 수 있다는 보장은 없습니다.
예를 들어 전체 사진 갤러리를 다운로드 했지만 사용자가 첫번째 이미지만 본 후 사용자가 나갔을 때 웹 페이지에서는 메모리 및 대역폭 낭비로인해 발생합니다.
페이지에 액세스할때 모든 콘텐츠를 대량으로 로드하는 대신 사용자가 필요 할때만 시렞 콘텐츠로 대체 됩니다.

lazy-laodingd을 사용하면 페이지가 placeholder 콘텐츠로 작성이 되며 , 사용자가 필요할때만 실제 콘텐츠로 대체 된다.
last-loading은 페이지를 일겅들이는 시점에 중요하지 안흥ㄴ 리소스를 로딩을 추 후에 하는 기술입니다. ㅇ대신에 이 중요하지 않은 리소스들은 필요할때 로드 되어야합니다.
이미지와 관련된 경우에는 중요하지 않은 리소스들은 ‘off-screen’와 함께 동기화됩니다.
누군가가 웹 페이지(이미지 , 비디오 등)에 리소스를 추가할때 , 리소스는 small placeholder를 참조합니다. 사용자가 웹페이지를 검색할때 실제 리소스는 브라우저에 의해 캐시되고 ㅇ리소스가 사용다 화면에 표시될때 placeholder을 대체합니다. 예를 들어 , 사용자가 웹페이지를 로드하고 즉시 남겨 두면 웹 페이지의 맨 위 부분 이외의 항복이 로드 되지 않습니다.
이미지, 비디오를 그냥 로딩하지 않고 lazy loading을 사뇬하는 이유는 사용자가 볼 수 없는 것들을 로딩할 가능성이 있기 때문입니다.

사용자가 보고 있지 않은 것들을 로딩할 때 데이터가 낭비가 됩니다.
미디어 리소스를 다운로드한 후 브라우저는 이를 디 코딩하여 화면에 렌더링 합니다.

SEO에 미치는 영향

lazy loading은 검색 엔진 순위에 영향을 미치낟. 리소스는 placeholder 콘텐츠여서 웹 사이트를 크롤링하는 검색엔진은 리소소의 내용을 잘못 해석하거나 무시할수 있습니다.
블로그 게시물과 같은 웹페이지의 전체 구성요소를 느리게 로드하면 검색엔진이 해당 구성요소을 우회하여 콘텐츠가 인덱싱 되지 않아 검색엔진 결과가 줄어들수 있습니다.
이러한 문제를 극복하는 한가지 방법은 lazy load하는 콘텐츠에 대한 링크를 제공하는 것 입니다. 이렇게하면 기본적으로 검색엔진 크롤러가 엑세스 할 수 있는 콘텐츠에서 웹페이지를 만듭니다. 검색엔진이 웹사이트를 index하면 이러한 링크를 따라가며 검색한 내용을 색인화 합니다. 이 방법은 기존저긍로 사용자가 콘텐츠를 동적으로 로드할 수 있데 하면서 lazy load도 웹 사이트를 전톤적인 웹사이트로 구성합니다.

장점

  1. lazy-loading은 콘텐츠 전달 최적화와 최종 사용자 간의 경험을 간소화하는 균형을 맞춥니다.
  2. 사용자가 처음 열 때 웹 사이트의 일부만 다운로드 해야하므로 사용자에게 콘텐츠를 더 빨리 제공합니다.
  3. 콘텐츠가 지속적ㅇ르로 사용자에게 공급되므로 사용자가 웹 사이트를 이탈할 확률을 낮출 수 있습니다.
  4. 사용자가 한번에 필요한 경우에만 콘텐츠를 불러오므로 리소스 사용(사용자의 배터리 , 시스템 간, 시스템 리소스)이 낮아진다.
1
<img data-src="https://ik.imagekit/demo/default-image.jpg" />

src 대신 data-src을 사용한다. 이미지가 로드되는 것을 막을 것이므로 브라우저에 로드할 시기를 알려줘야한다.이를 위해 이미지가 뷰포트에 입력되는 즉시 트리거 하는지 확인한다. 이미지가 뷰 포트에 들어갈때 확인하는 방법은 두가지 방법이 있다.

  1. Javascript 이벤트를 통한 방법

    • 브라우저에서 이벤트 리스너를 resize,scroll 및 orientationChnge 이벤트에 사용한다. 스크롤 이벤트는 스크롤이 발생할때 사용자가 페이지에 는 위치를 감시하므로 매우 명확하다.
      resize 및 orientationChange 이벤트도 똑같이 중요하다. 브라우저 창 크기가 변경되면 resize 이벤트가 발생하지만 장치가 가로 방향에서 세로방향으로 회전할때 orientaionChange가 트리거 되거나 그 반대의 경우에도 orientationChange가 트리거 된다. 우리는 화면의 변화에 인식하고 화면에 표시 될 이미지의 수를 결정하고 그에 따라 로드를 실행하는데 세가지 이벤트를 사용할 수 있다.
      스크롤 할때 스크롤 이벤트가 여러번 빠르게 트리거 된다. 따라서 성능을 위해 자연로드 기능 실행을 제한하는 스크립트에 작은 시간 초과를 추가하여 브라우저의 동일한 스레드에서 실행중인 다른 작업을 차단하지 않는다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <img src="https://ik.imagekit.io/demo/img/image1.jpeg?tr=w-400,h-300" />
    <img src="https://ik.imagekit.io/demo/img/image2.jpeg?tr=w-400,h-300" />
    <img src="https://ik.imagekit.io/demo/img/image3.jpg?tr=w-400,h-300" />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300"
    />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image5.jpeg?tr=w-400,h-300"
    />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image6.jpeg?tr=w-400,h-300"
    />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image7.jpeg?tr=w-400,h-300"
    />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image8.jpeg?tr=w-400,h-300"
    />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image9.jpeg?tr=w-400,h-300"
    />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image10.jpeg?tr=w-400,h-300"
    />
    1
    2
    3
    4
    5
    6
    7
    8
    img {
    background: #f1f1fa;
    width: 400px;
    height: 300px;
    display: block;
    margin: 10px auto;
    border: 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    document.addEventlistener('DOMContentLoaded',function (){
    var lazyloadImages = document.querySelector('img .lzy ');
    var lazyloadThrottleTimeout;

    function lazyload(){
    if(lazyloadThrottleTimeout){
    cleartimeOut(lazyloadThrottleTimeout);
    }
    lazyloadThrottleTimeout = setTimeout(()=>{
    var scrollTop= window.pageyOffset;
    lazyloadImages.forEach((img)=>{
    if(img.offsetTop< (window.innerHeight + scrollTop)){
    img.src = img.dataset.src;

    img.classList.remove('lazy');
    }
    });
    if(lazyloadImages.length === 0){
    document.removeEventListener('scroll',lazyload);

    window.removeEventListener('resize',lazyload);
    window.removeEventListener('orientationChange',lazyload);
    }
    },20)
    }
    document.addEventListener('scroll'lazyload);
    window.addEventListener('resize',lazyload);
    window.addEventListener('orientationChange'lazyload);
    })

    이 예제에서 처음의 세개의 이미지는 앞에 로드됩니다. URL은 data-src 속성대신 src속성에 직접 표시한다.이는 우수한 사용자 경험을 위해서 필수적이다. 이러한 이미지는 페이지 상단에 있으므로 최대한 빨리 표시해야한다. javascript을 로드할때 까지 기다릴 필요가 없다.

    2.Insersection Observer API 사용

    엘리먼트가 뷰포트로 들어갔을때 감지하고 엘리먼트가 뷰포트에 있을때 액션을 취하는 것을 간단하게 한다.
    이전 방법에서는 이벤트를 바인딩하고 성능을 염두에 두고 요소가 뷰포트에 있는지를 여부를 계산하는 방법으로 구현해야했다. intersection observer API는 계산을 피하고 뛰어난 성능을 제공함으로써 오버헤드를 모두 제거합니다. isIntersecting 속성을 사용하여 요소가 뷰포트에 들어간 것을 감지하면 data-src 특성에서 URL을 선택하고 브라우저의 src 특성으로 이동하여 이미지로드를 트리거 합니다.
    이것이 끝나면 이미지에서 lazy-loading 클래스를 제거하고 해당이미지에서 observer를 제거합니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <img src="https://ik.imagekit.io/demo/img/image1.jpeg?tr=w-400,h-300" />
    <img src="https://ik.imagekit.io/demo/img/image2.jpeg?tr=w-400,h-300" />
    <img src="https://ik.imagekit.io/demo/img/image3.jpg?tr=w-400,h-300" />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300"
    />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image5.jpeg?tr=w-400,h-300"
    />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image6.jpeg?tr=w-400,h-300"
    />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image7.jpeg?tr=w-400,h-300"
    />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image8.jpeg?tr=w-400,h-300"
    />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image9.jpeg?tr=w-400,h-300"
    />
    <img
    class="lazy"
    data-src="https://ik.imagekit.io/demo/img/image10.jpeg?tr=w-400,h-300"
    />
    1
    2
    3
    4
    5
    6
    7
    8
    img {
    background: #f1f1fa;
    width: 400px;
    height: 300px;
    display: block;
    margin: 10px auto;
    border: 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    document.addEventListener('DOMContentLoaded',()=>{
    var lazyloadImages;
    if("InsersectionObserver" in window){
    lazyloadImages = document.querySelectorAll('.lazy');
    var imageObserver = new IntersectionObserver((entries,observer)=>{
    entries.forEach(entry=> {
    if(entry.isintersecting){
    var image = entry.target;
    image.src = image.dataset.src;
    images.classList.remove('lazy');
    imageObserver.unobserve(image);
    }
    });
    });
    lazyloadImages.forEach((image)=>{
    imageObserver.observe(image);
    });
    }
    else{
    var lazyloadTrottleTimeout;
    laztloadImages = document.querySelectorAll('.lazy');
    function lazyload(){
    if(lazyloadThrottleTimeout){
    clearTimeout(lazyloadThrottleTimeout)
    }
    lazyloadThrottleTimeout = setTimeout(()=>{
    var scrollTop = window.pageYOffset;
    lazyloadImages.forEach((img)=>{
    if(img.offsetTop < window.innerHeight +scrollTop){
    img.src = img.dataset.src;
    img.classList.remove('lazy');
    }
    });
    if(lazyloadImages.length ==== 0){
    document.removeEventListener('scroll', lazyload);
    window.removeEventListenr('resize',lazyload);
    window.removeEventListener('orientationChange',lazyload);
    }
    },20)
    }
    document.addEventListener('scroll',lazyload);
    window.addeEventListenr('resize',lazyload);
    window.addEventListner('orientationChange',lazyload);
    }
    });

    두가지 방법의 이미지로드 시간을 비교하면 intersection Observer API를 사용하여 이미지 로드가 훨씬 빨리지고 작업이 더 빨리 시작된다는것을 알 수 있습니다.
    이벤트 리스너가 포함된 메서드에서는 성능을 높이기 위해 시간 제한을 추가해야했지만 이미지 로드가 약간 지연이 되면서 사용자 환경에 약간 부정적인 영향을 줍니다. 하지만 모든 브라우저에서 지원하는것은 아닙니다.
    따라서 intersection Observer API가 지원되지 않는 브라우저에서 이벤트 리스너 메소드로 폴백해야합니다. 우리는 위의 예제에서 이것을 고려해서 if else문으로 나눴습니다.