프로젝트가 어느정도 마무리 되고 시간적인 여유가 생겨 웹 성능 최적화를 위해 ImagelazyLoaidng을 적용해보았다.

✅ lazyLoading을 적용하면

1) 해당 페이지의 모든 이미지를 처음에 다운로드 하지 않으므로 페이지를 빠르게 로드할 수 있을거고
2) 페이지가 빠르게 로드 되는 만큼 사용자는 콘텐츠를 더 빠르게 볼 수 있을거라 생각했다.
3) 물론 나는 lazyLoading가 웹 성능 최적화에 쓰인다길래 한 번 적용해보고 싶어던 마음이 크다 힣

lazyLoading을 구현할 때 Intersection Observer라는 Web API를 많이 사용한다길래 나도 해당 API를 활용해서 구현해봤다.

Intersection Observer로 요소의 관찰자를 등록하고, 요소가 화면에 나타날 때 수행할 작업을 작성해주면 끘!

✅ 적용된 결과 미리 보기

개발자도구 > network창 > img 탭 눌러 확인해보면 이미지를 한 번에 불러오는게 아닌 필요한 시점에 불러오는걸 확인할 수 있다. 👏🏻

Intersection observer란?

Intersection observer는 내가 설정한 요소(Element)가 화면에 지금 보이는 요소인지 아닌지를 구별하는 기능을 제공한다.

new IntersectionObserver()를 통해 관찰자(Observer)를 생성하고 observe메서드를 사용해서 관찰할 대상(Element)을 지정한다.

const io = new IntersectionObserver(callback, options) // 관찰자 초기화
io.observe(element) // 관찰할 대상(요소) 등록

https://heropy.blog/2019/10/27/intersection-observer/ 글을 참고!
intersection Observer에 대해 아주 간략하게 적어놨다.
더 궁금한게 생기면 해당 블로그에서 찾아보면 될 듯!

Intersection observer를 활용한 ImagelazyLoading 적용기

new IntersectionObserver() 로 생성자를 만들어주는데, 2개의 인수 callback과 options을 정해줘야한다.

1. option: callback함수가 호출되는 상황을 정의

IntersectionObserver의 인스턴스에서 callback함수가 호출되는 상황을 정의해주면 된다.

// options 설정
const options = {
  root: null, // null일 경우 브라우저 viewport
  // root: document.querySelector('.class이름') class를 가진 엘리먼트를 root로 설정하고싶을때
  rootMargin: [0,0,'100px',0], // 아래로 100px까지는 관찰 대상으로 간주
  threshold: 0.5 /// 관찰 대상의 50% 이상이 보일 때 콜백 호출
}

2. callback: 함수가 호출됐을 때 실행할 로직 정의

관찰할 대상이 등록되거나, 가시성에 변화가 생기면 관찰자는 callback을 실행한다.
나는 isIntersecting 이라는 속성을 사용해서 내가 설정한 Option에 해당되는 교차상태에 따라 코드가 실행되게 만들었다.


// IntersectionObserver 생성
const io = new IntersectionObserver((entries) => {
  // IntersectionObserverEntry 객체 리스트와 observer 본인(self)를 받음
  // 동작을 원하는 것 작성
  entries.forEach(entry => {
    // 관찰 대상이 viewport 안에 들어온 경우 image 로드
    if (entry.isIntersecting) {
      // data-src 정보를 타켓의 backgroundImage 속성에 설정
      loadBackgroundImage()
      // 이미지를 불러왔다면 타켓 엘리먼트에 대한 관찰을 멈춘다.
      observer.unobserve(entry.target);
    }
  }
}, options)

2-1. 내가 설정한 요소(Element)가 화면에 보일 때 실행할 로직

나는 loadBackgroundImage() 메소드를 만들어 내가 설정한 요소가 화면에 보일 때 실행할 로직을 만들었다.
loadBackgroundImage() 메소드의 내용은
설정한 요소에 dataset을 활용해서 이미지를 불러올 URL을 설정해놨는데,
관찰 대상이 교차상태가 되었을 때 해당 요소의 className이 'lazy'라면 설정한 dataset 정보를 가져와 backgroundImage로 설정하는 내용이다.

// HTML
<div 
 :data-imageUrl="item[fieldNames.imageUrl] ? item[fieldNames.imageUrl] : ''">
// loadBackgroundImage()
const loadBackgroundImage = () => {
      if (el.className.includes('lazy')) {
        // 선택한 요소의 클래스가 'lazy'라면 dataset에서 imageURL 데이터를 가져와 backgroundImage로 설정해준다.
        const imageUrl = el.dataset.imageurl;
        el.style.backgroundImage = `url(${imageUrl})`;
      }
    };

3. 실전코드 및 적용된 이미지

export default {
  mounted(el) {
    const loadBackgroundImage = () => {
      if (el.className.includes('lazy')) {
        const imageUrl = el.dataset.imageurl;
        el.style.backgroundImage = `url(${imageUrl})`;
      }
    };

    // Intersection Observer를 지원하는 브라우저의 경우
    // Observer 관찰자 만들어 
    // 스크롤에 따라 이미지 로딩할 수 있도록 만들어줌 
    const createObserver = () => {
      const observerOption = {
        root: null, // 뷰포트를 기준으로 관찰
        rootMargin: '0px 0px 100px 0px', // 아래로 100px까지는 관찰 대상으로 간주
        threshold: 0.5, // 관찰 대상의 50% 이상이 보일 때 콜백 호출
      };
      const observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            loadBackgroundImage();
            observer.unobserve(el);
          }
        });
      }, observerOption);
      observer.observe(el)
    };
    
    // Intersection Observer를 지원하지 않는 브라우저의 경우
    // backgroundImage에 바로 이미지 로딩시켜줌

    window['IntersectionObserver']
      ? createObserver()
      : loadBackgroundImage();
  },
};

위와 같이 적용을 하면
1. ImagelazyLoaidng을 적용하기로 지정한 요소가 뷰포트내에 보이기 시작하면
2. loadBackgroundImage()가 실행되면서 지정한 요소내의 dataset에 담겨있는 이미지 URL 경로를 가져와 el.style.backgroundImage = url(${imageUrl}) 처럼 css를 변경한다.
3. 변경된 backgroundImage의 이미지 주소에서 이미지가 로딩되면서 ImagelazyLoaidng이 구현된다.


vue 프로젝트와 관련해서 lazyLoading 하는 방법을 찾다보니
cumstomDirective를 통해 lazyLoading을 구현하는 방법도 있길래 한 번 적용해봤다.

이건 길어지니까 따로 링크 걸어두는걸루!
🔗 vue3 customDirective 적용기

profile
적응중

0개의 댓글