IntersectionObserver 사용해보기 (feat. React)

김학재·2021년 2월 16일
0

프론트엔드

목록 보기
7/12

IntersectionObserver를 어떻게 사용하는 지 알았으니 이제 본격적으로 웹사이트에 적용할 차례이다.

문제 상황

lazy load 적용 전 문제점
lazy loading을 적용하기 전 웹 사이트의 모습이다. 극단적인 예시를 위해 네트워크 상황을 Fast 3G로 설정하였다.
웹 페이지가 로딩됨과 동시에 페이지의 이미지 파일을 전부 다운로드하고 이들이 순서대로 나타나는 것을 확인할 수 있다.

IntersectionObserver 사용

페이지 내의 모든 이미지 파일에 IntersectionObserve를 사용하기 위해 이미지마다 observer를 부착시킨 뒤 IntersectionObserver를 실행시킨다.

<img src={props.presrc} alt={props.label+' 이미지'} className="cards__item__img" data-lazy={props.src}/>

이미지 태그는 위와 같이 설정하였다. src 속성에는 일종의 placeholder 이미지를 지정하고 실제 나타낼 이미지는 data-lazy 속성에 지정하였다.

const images = document.querySelectorAll("img");

const observer = new IntersectionObserver((entries, observer) => {
    entries.forEach((entry) => {
        if (!entry.isIntersecting) return;
        const image = entry.target;
        const src = image.getAttribute("data-lazy");
        image.setAttribute("src", src);
        observer.unobserve(image);
      })

images.forEach((image) => {
    observer.observe(image);
})

위와 같이 선언하면 모든 이미지들에 observer가 부착되며 isIntersecting 메서드를 통해 이미지의 src 속성이 바뀌게 된다.

React router에서의 사용

여기서 추가로 발생한 문제점이 바로 react router 때문이었다. router를 사용해 각 페이지를 구현했는데 해당 페이지에서 새로 고침 시 이미지가 로딩되지 않는 문제점이 발생했다.
처음에는 window.addEventListener('load', ~~)를 통해 해결하려 했으나 해당 이벤트는 최초 실행 시 즉, App.js가 최초로 실행 시에만 발생하고 라우터 페이지에서 새로 고침을 아무리 해도 해결되지 않았다.

lazy loading을 적용하지 말고 그냥 WebP 이미지를 활용해 최적화할까 생각하던 중 class Component를 사용해야겠다는 생각이 번쩍 들었다. (진짜 번개처럼 갑자기 생각났음)

기존의 구조

App
| - Bread (빵 목록 보여주는 페이지)
|   | - BreadList (실제 빵 종류들)
|   | - Footer
| - Vegetable
|   | - VegetableList
|   | - Footer
| - Cheese
|   | - CheeseList
|   | - Footer
| - Sauce
    | - SauceList
    | - Footer

기존에는 위 구조를 모두 function component로 선언했다. 여기서 이미지가 렌더링되는 파일은 ~List인데 이 파일들을 모두 class component로 바꿔주었다.
이렇게 되면 다양한 lifecycle 메서드의 활용이 가능하다. 실제로도 확인해 보니 새로고침을 해도 componentDidMount 메서드는 항상 호출되는 것을 확인할 수 있었다.

최종 class component의 코드는 대략 다음과 같다.

class BreadList extends React.Component {
  // componentDidMount 내에 IntersectionObserver를 선언
  // 새로 고침 시에도 이 부분이 항상 실행됨. 문제 해결!
  componentDidMount() {
    const images = document.querySelectorAll("img");

    const observer = new IntersectionObserver((entries, observer) => {
      entries.forEach((entry) => {
        if (!entry.isIntersecting) return;
        const image = entry.target;
        const src = image.getAttribute("data-lazy");
        image.setAttribute("src", src);
        observer.unobserve(image);
      })
    })

    images.forEach((image) => {
      observer.observe(image);
    })
  }

  render() {
    return (
        <div>빵 목록들</div>
    )
  }
}

결과

lazy load 적용 후
이전과 달리 이미지가 로딩되기 전 로딩을 나타내는 이미지가 나타나며, 스크롤을 내릴 시에만 필요한 이미지가 추가로 로딩되는 것을 확인할 수 있다. 또한, 새로 고침 시에도 문제 없이 이미지가 로딩된다.


복잡한 내용은 아니지만 실제 웹 사이트에 적용을 하려니 생각보다 시간이 많이 걸렸다. 더군다나 리액트의 특성까지 문제를 일으켜서 해결책을 찾는데 고심했다.

하지만 이렇게 해결하고 나니 이제 진짜 내 것이 되었다는 생각이 든다!😁

profile
YOU ARE BREATHTAKING

0개의 댓글