지난 Intersection Observer로 무한 스크롤 구현하기에 이은 React와 Intersection Observer를 사용하여 lazy image load 구현하기를 작성해보려 한다.
반 년 전 (이제는 전회사가 된) 회사 면접 과제를 하며 무한 스크롤을 구현했는데 이직을 준비하며 또 다른 회사의 면접 과제로 lazy image를 기능 구현이 있어 다시 Intersection Observer를 사용해보았다. 인생..
MDN에서 나열한 IntersectionObserver를 적용할 수 있는 다양한 상황들 중..
Lazy-loading of images or other content as a page is scrolled.
페이지 스크롤 시 이미지를 Lazy loading할 때
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을 통해 스크롤을 하며 새로운 콘텐츠를 불러올 때
Reporting of visibility of advertisements in order to calculate ad revenues.
광고의 수익을 계산하기 위해 광고의 가시성을 참고할 때
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 hookref
를 반환한다.ref.current
를 observe하여 타겟이 isIntersecting이 된 경우 인자로 넘겨받은 image src로 상태 값을 업데이트 한다.imageSrc
반환하여 컴포넌트에서 렌더링 할 수 있도록 한다. 인자로 넘겨받은 src
을 viewport에 들어왔을 때 보여주기 위해 상태로 관리한다고 볼 수 있다
작성한 코드는 아래와 같다.
LazyImage
component위에서 작성한 커스텀 훅을 사용한 LazyImage
컴포넌트를 만들었다.
useLazyImageObserver
훅에 prop으로 넘겨받은 src를 인자로 넘겨준다.useLazyImageObserver
에서 반환되는imageRef
을 사용해 ref
를 잡아 타겟을 지정할 수 있도록 한다.useLazyImageObserver
에서 반환되는 imageSrc
를 이미지 태그의 src
로 넣어준다.사실 이미지는 왠만하면 lazy load 하는게 좋다고 생각해서 굳이 LazyImage
라고 하지 않아도 되는 게 아닐까? 하는 생각이 들기도 한다.. 아무튼 작성한 코드는 아래와 같다.
개발자 도구 > 네트워크 탭 > img를 선택하여 이미지 로드만 확인해보자
lazy image가 적용되지 않은 경우, 스크롤 이동 즉 viewport 노출과 관계없이 로드되는 것을 확인할 수 있다.
반면 lazy image가 적용된 경우, 스크롤 이동에 따라 viewport에 노출되는 순간 순차적으로 로드되는 것을 확인할 수 있다.
과제시 제공 받은 이미지는 lorem picsum으로 대체하였다 ㅎㅎ.. 근데 gif가 왜 안올라가지..ㅎㅎ
과제 시에는 opacity 값을 주어 기본 이미지 없이 작업했는데 css만으로 작업할 경우 이미지가 뜰지 예측하기 어렵고 서버 응답이 느리거나 에러가 나는 경우 대체 이미지가 없으므로 ux적으로 기본 이미지를 넣어주는 게 좋다고 생각한다. ux 공부하면 재밌을 것 같다..
기존 코드와 달라진 건 거의 없고, initial state 값에 기본 이미지를 넣어주고 조건문에서 imageSrc
유무 확인만 제거했다.
gif가 안올라가네요.. 뉴뉴..
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
좋은 글 잘 봤습니다.