Intersection Observer로 Lazy Image 구현

김혜진·2020년 9월 22일
19

javascript

목록 보기
8/9

지난 Intersection Observer로 무한 스크롤 구현하기에 이은 React와 Intersection Observer를 사용하여 lazy image load 구현하기를 작성해보려 한다.

반 년 전 (이제는 전회사가 된) 회사 면접 과제를 하며 무한 스크롤을 구현했는데 이직을 준비하며 또 다른 회사의 면접 과제로 lazy image를 기능 구현이 있어 다시 Intersection Observer를 사용해보았다. 인생..

어디에 적용할 수 있을까?

MDN에서 나열한 IntersectionObserver를 적용할 수 있는 다양한 상황들 중..

  1. Lazy-loading of images or other content as a page is scrolled.
    페이지 스크롤 시 이미지를 Lazy loading할 때

  2. Implementing "infinite scrolling" web sites, where more and more content is loaded and rendered as you scroll, so that the user doesn't have to flip through pages.
    Infinite scrolling을 통해 스크롤을 하며 새로운 콘텐츠를 불러올 때

  3. Reporting of visibility of advertisements in order to calculate ad revenues.
    광고의 수익을 계산하기 위해 광고의 가시성을 참고할 때

  4. Deciding whether or not to perform tasks or animation processes based on whether or not the user will see the result.
    사용자가 결과를 볼 것인지에 따라 애니메이션 동작 여부를 결정할 때

출처 MDN

이번엔 1번에 해당하는 상황에 적용하게 되었다. Intersection Observer에 대한 것은 이전 포스팅에 간략하게 적어두었으니 실제 구현에 관해서 초점을 맞추려고 한다.

 

어떻게 적용할 수 있을까?

과제를 React로 구현하여 custom hook을 만들어 사용하였는데, IntersectionObserver 자체는 web API로 제공되는 것이므로 js를 사용한다면 어디든 적용 가능하다.

useLazyImageObserver custom hook

  1. observe하고자 하는 타겟을 잡을 수 있게ref를 반환한다.
  2. 해당 타겟이 viewport에 들어왔을 때 로드 될 src를 인자로 받는다.
  3. ref.current를 observe하여 타겟이 isIntersecting이 된 경우 인자로 넘겨받은 image src로 상태 값을 업데이트 한다.
  4. 위에서 업데이트 된 상태 값imageSrc 반환하여 컴포넌트에서 렌더링 할 수 있도록 한다.

인자로 넘겨받은 src을 viewport에 들어왔을 때 보여주기 위해 상태로 관리한다고 볼 수 있다

작성한 코드는 아래와 같다.

useLazyImageObserver code

 

LazyImage component

위에서 작성한 커스텀 훅을 사용한 LazyImage 컴포넌트를 만들었다.

  1. useLazyImageObserver 훅에 prop으로 넘겨받은 src를 인자로 넘겨준다.
  2. useLazyImageObserver에서 반환되는imageRef을 사용해 ref를 잡아 타겟을 지정할 수 있도록 한다.
  3. useLazyImageObserver에서 반환되는 imageSrc를 이미지 태그의 src로 넣어준다.

사실 이미지는 왠만하면 lazy load 하는게 좋다고 생각해서 굳이 LazyImage라고 하지 않아도 되는 게 아닐까? 하는 생각이 들기도 한다.. 아무튼 작성한 코드는 아래와 같다.

LazyImage component code

 

결과

개발자 도구 > 네트워크 탭 > img를 선택하여 이미지 로드만 확인해보자

개발자 도구 탭

lazy image loading 미적용시

lazy image가 적용되지 않은 경우, 스크롤 이동 즉 viewport 노출과 관계없이 로드되는 것을 확인할 수 있다.
lazy loading 미적용

lazy image loading 적용시

반면 lazy image가 적용된 경우, 스크롤 이동에 따라 viewport에 노출되는 순간 순차적으로 로드되는 것을 확인할 수 있다.
lazy loading 적용

과제시 제공 받은 이미지는 lorem picsum으로 대체하였다 ㅎㅎ.. 근데 gif가 왜 안올라가지..ㅎㅎ

 

이 외

- 기본 이미지에서 로드된 이미지로 교체하기

과제 시에는 opacity 값을 주어 기본 이미지 없이 작업했는데 css만으로 작업할 경우 이미지가 뜰지 예측하기 어렵고 서버 응답이 느리거나 에러가 나는 경우 대체 이미지가 없으므로 ux적으로 기본 이미지를 넣어주는 게 좋다고 생각한다. ux 공부하면 재밌을 것 같다..
기본 이미지

기존 코드와 달라진 건 거의 없고, initial state 값에 기본 이미지를 넣어주고 조건문에서 imageSrc 유무 확인만 제거했다.

gif가 안올라가네요.. 뉴뉴..

 

- infinite scroll을 사용하면 자동 lazy image loading이 되는 것 아닐까?

infinite scroll은 특정 DOM이 뷰포트 내에 들어왔을 때 다음 데이터를 불러오게끔 하므로 불러오기 전에는 데이터 자체가 없어서 렌더할 수 없고 그렇다보니 당연히 이미지 로드가 되지 않는다. 그러나 데이터를 가져오는 것과 이미지를 화면에 그리는 것은 별개의 일이므로 스크롤 이벤트로 lazy image loading을 구현한다고 할 수는 없다.

예를 들어 스크롤 이벤트를 통해 한번에 50개씩 데이터를 받는다고 할 때, 50개씩 데이터 요청 하는 것이 infinite scroll로, 응답된 50개의 이미지를 바로 그리지 않고 화면 상에 보여질 차례가 되었을 때 그리는 것이 lazy image loading 인 것이다. 뭔가 쓰고 보니 너무 당연한 말..ㅎㅎ

종종 찾는 Pinterest 사이트에 접속해보면 화면 중반쯤 스크롤이 달했을 때 다음 데이터를 불러와 스크롤 사이즈가 작아지지만 불러온 데이터 내의 이미지들은 바로 그려지지 않고 스크롤을 이동하며 화면 상에 나타날 위치가 되었을 때 이미지가 보여진다.
개발자 도구 > 네트워크 탭 > img를 선택하여 확인할 수 있다.

...

브라우저는 이미지 로드 이외에도 여러 작업을 진행해야 하므로, 상황에 맞게 필요한 작업들이 우선적으로 진행될 수 있게 하는 것이 중요하다. 그리고 실제 사용자는 모든 데이터를 보지 않고 화면을 이탈하는 경우가 정말 잦다. 언젠가 나 스스로 어떻게 웹사이트를 브라우징하는지 의식한 후에 좀 놀람..

lazy image loading 작업을 함으로써 화면상에 아직 보여지지 않는 부분에 대해 불필요한 네트워크 작업을 하지 않을 수 있다.

 

참고


Intersection Observer API
The Image Embed element
Timing element visibility with the Intersection Observer API
Native image lazy-loading for the web
Five Ways to Lazy Load Images for Better Website Performance

profile
꿈꿀 수 있는 개발자가 되고 싶습니다

1개의 댓글

comment-user-thumbnail
2023년 2월 7일

좋은 글 잘 봤습니다.

답글 달기