필요한 시점에 필요한 리소스 가져오기 : Lazy loading

nnm·2020년 3월 29일
49
post-thumbnail

프로그래머스 2020 Dev-Matching : 웹 프론트엔드 과제 복기
https://github.com/woohyeonjo/ilovecat

단순 글 보다는 이미지나 동영상이 더 눈길을 끌고 정보 전달력도 좋기 때문에 웹 페이지에도 상당히 많은 이미지나 동영상 리소스가 포함되어있다. 그런데 용량이 매우 크거나 굉장히 많은 양의 리소스를 포함하고 있는 웹 페이지는 불러올 때 네트워크 비용이 클 뿐만아니라 서비스의 속도를 저해할 수 있다. Lazy loading을 사용하면 이런 문제 상황들을 개선할 수 있다.

Lazy loading은 유저가 바라보는 화면 밖 영역을 중요하지 않은 영역으로 규정하고 이 영역에 대한 로딩을 늦추는 것이다. 그렇다면 Lazy loading을 사용하는 것에 구체적으로 어떤 이점이 있을까?

  • 유저가 보지 않을 영역의 리소스는 로딩하지 않기 때문에 데이터를 절약할 수 있다.(데이터가 제한된 상황에서는 큰 이점이다.)
  • 리소스 다운로드 후 브라우저는 디코딩 및 렌더링 과정을 거치게 되는데 이 때 발생하는 시간, 배터리 등의 시스템 리소스를 절약한다.

이번 프로젝트에서는 고양이를 검색했을 때 고양이의 정보와 함께 이미지를 불러오게 되는데 검색어에 따라 데이터의 양이 굉장히 늘어날 수 있었다. 따라서 수 많은 고양이 사진이 한 번에 로드되는 것을 대비하여 Lazy loading을 도입할 필요성이 있었다. Lazy loading을 구현하는 방법에는 크게 두 가지가 있다.

  • getBoundingClientRect를 이용해 엘리먼트가 화면안에 들어왔을 때 로딩하는 방법
  • Intersection Observer를 이용하여 엘리먼트를 관찰하는 방법

이번에는 Intersection Observer를 이용하는 방법으로 구현해보았다.

  export function lazyLoad() {
      const lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));

      if ("IntersectionObserver" in window) {
          let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
              entries.forEach(function(entry) {
                  if (entry.isIntersecting) {
                      let lazyImage = entry.target;
                      lazyImage.src = lazyImage.dataset.src;
                      lazyImage.classList.remove("lazy");
                      lazyImageObserver.unobserve(lazyImage);
                  }
              });
          });

          lazyImages.forEach(function(lazyImage) {
              lazyImageObserver.observe(lazyImage);
          });
      }
  }
  • Lazy loading을 위한 Intersection Observer를 만들고 lazy loading을 수행할 모든 엘리먼트들을 감시하도록 한다.
  • [].slice.call
  • dataset
  export default class ResultsSection {
      constructor({$target, data, onClick, onScroll}) {
          ...
          lazyLoad();
          ...
      }

      setState(data) {
          ...
          lazyLoad();
          ...
      }
  } 
  • ResultsSection이 생성될 때 그리고 상태가 변했을 때 lazyLoad()를 호출한다.
  • 이전 검색 결과를 새로고침 후에도 유지하는 기능이 있기 때문에 생성자에서 lazyLoad() 호출이 필요하다.
  • 상태가 변화할 때 마다 새롭게 랜더링하기 때문에 lazyLoad()를 호출해야한다.
profile
그냥 개발자

6개의 댓글

comment-user-thumbnail
2020년 3월 29일

벨로그도 레이지 로딩 적용 해야겠어요..
미루고만 있었는데 이를 통해서 서버 측에서도 비용을 아낄 수 있다면 꼭 해야 할 것 같습니다.
나중에 적용하게 되면, 저도 포스트로 공유해볼게요!

1개의 답글
comment-user-thumbnail
2020년 3월 30일

와 이렇게 복기하시는거 진짜 대단하신거 같습니다. 저도 복기해야지 해야지 하다가 계속 미루고 있었는데.. 작성해주신 코드 보면서 많이 배우고 가겠습니다!

1개의 답글
comment-user-thumbnail
2020년 4월 10일

좋은 글 감사합니다!

답글 달기
comment-user-thumbnail
2020년 4월 20일

와 감사합니다.

답글 달기