서버에서 받아오는 단어를 보여주는 모달의 성능 개선을 위해 리팩토링을 진행하면서, 간단히 무한스크롤을 구현할 수 있게 해주는 intersection observer API
의 존재를 알게 되었다.
직접 사용해보니 생각보다 간단했고 scroll
이벤트를 직접 활용하는 것보다 성능상 이점도 있는 것 같아 해당 API란 무엇인지, 어떻게 구현했는지, 각 옵션들에 대해 알아보려고 한다.
root
엘리먼트와 target
엘리먼트의 교차점을 관찰하여 교차점의 변경사항을 비동기적으로 관찰하는 방법을 제공한다.
MDN에서는 해당 API를 다음과 같은 상황에서 사용할수 있다고 설명하고 있다.
- 페이지 스크롤시 Lazy-loading 이 필요할때
- 무한 스크롤 구현
- 광고 수익률 계산을 위해 광고 가시성을 파악할 때
- 사용자가 결과를 볼 수 있는지에 따라 작업 또는 애니메이션을 수행할지에 대한 여부를 결정할 때
let options = {
root: document.querySelector("#scrollArea"),
rootMargin: "0px",
threshold: 1.0,
};
let observer = new IntersectionObserver(callback, options);
let target = document.querySelector("#listItem");
observer.observe(target);
생성자 키워드를 통해 인스턴스를 생성하고, callback
과 options
를 인자로 넘기는 것을 볼 수 있는데 options
에는 어떤 기능이 있는지 각각 살펴보았다.
target
의 부모 요소중 교차 여부를 판단할 대상을 설정한다. 기본값은 null
로, 별도로 설정하지 않으면 기본값이 적용되어 문서의 뷰포트를 기준으로 대상 요소의 변화를 관찰한다.
root
의 margin 값을 설정해주는 옵션으로 기본값은 0이다. 이름 그대로 margin을 통해 root
요소 범위를 확장할 수 있으며, 단 값 입력시 단위를 꼭 입력해 주어야 한다.
root
와 target
의 교차가 이루어짐을 판단하는 기준으로 0에서 1까지 설정 가능하다. 기본값은 0이며, 만약 0.9로 설정할 경우 target
이 90%만큼 보여질 때 교차되었다고 판단한다.
눈으로 확인해보고 싶어서 직접 threshold
가 0일때와 0.9일때를 비교해 보았다.
직접 확인해보니 threshold
가 0일때는 viewport
에 target
이 보여지는 순간 바로 인식하는 것을 확인할 수 있었고, 0.9일때는 90% 보여졌을 때 인식하는 것을 확인할 수 있었다.
Intersection Observer API
는 브라우저에서 제공되는 WEB API이기 때문에 별도 라이브러리를 설치할 필요 없이 사용할수 있지만, React
에서 해당 API를 hook
형태로 간편하게 구현할 수 있는 라이브러리가 있어 사용해 보았다.
import { useInView } from 'react-intersection-observer';
function MyComponent() {
const [ref, inView] = useInView();
useEffect(() => {
if (inView) {
setPageNumber(prev => prev + 1);
}
}, [inView]);
return (
<div ref={ref}>
{inView ? 'target 보인다' : 'target 안보인다'}
</div>
);
}
React에서는 target
이 되는 element에 ref를 달아서 target을 관찰한다.
코드를 보면 알 수 있듯, target
요소가 root
와 교차되는 시점에 inView
가 true
로 변한다.
실제 리팩토링을 진행할땐 해당 라이브러리를 사용해 보았는데, useEffect
로 스크롤이 리스트의 상단 또는 하단에 닿을때마다 pageNumber
를 증감시켜 페이지 넘버에 따라 단어를 삭제하고 받아올 수 있도록 구현했다.
observer API를 사용하지 않고 특정 element가 보이는 순간 어떤 동작을 수행해야 할 경우에 주로 scroll
이벤트를 등록하고 스크롤이 해당 element 위치에 도달한 순간 콜백함수를 등록하는 방법을 사용할 것이다.
하지만 스크롤 이벤트로 직접 처리할 경우 고려해야 할 점이 있다.
대화형 사이트에서 갱신하고 난 이후의 경우와 같이 브라우저가 웹 페이지의 위치와 기하학적 구조를 다시 계산할 때 리플로우가 발생합니다. 그 다음에는, 브라우저가 웹 페이지를 다시 그려 결과적인 시각적 갱신을 표시하는 리페인트가 따라옵니다.
Reflow - MDN
스크롤을 이동할때마다 매우 많은 이벤트가 발생하면서 레이아웃을 다시 계산해야 하는 리플로우가 발생하고, DOM에 리페인트 과정을 거치면서 다시 그려지는데 이는 비용이 매우 큰 작업이므로 성능 저하에 주의해야 한다.
이런 문제점들을 개선하려 쓰로틀링, 디바운싱을 이용해 성능 개선을 하기도 한다.
해당 API는 스크롤 이벤트와 다르게 비동기 적으로 실행되어 메인 스레드에 영향을 주지 않으며 가시성 변경 모니터링이 가능하다.
스크롤로 여러 요소에 이벤트를 등록해야 할 경우 일일히 이벤트 핸들러를 추가해줘야 하는 반면 observer
를 활용하면 보다 쉽게 여러 요소의 모니터링이 가능한 것을 확인할 수 있었고,
특히 lazy-loading
이나 무한스크롤같은 기능을 구현할 땐 스크롤 이벤트로 처리하는 것보다 api를 사용하는게 비교적 간단하면서 성능상으로도 나은 것 같다고 느꼈다