무한스크롤을 구현하는데 라이브러리가 존재하긴 했지만, Intersection Observer API를 사용하여 만들 수 있다고 하여 시도해봤다. 그렇다면 우선 Intersection Observer가 무엇인지부터 살펴보도록 하자.
Web API중 하나인 Intersection Observer API는 크롬 51버전부터 사용할수 있으며, 2016년 구글 개발자 페이지를 통해 소개되었다고 한다. MDN에서는 Intersection Observer의 필요성을 아래와 같은 예를 들어 설명하고 있다.
정리하자면, Intersection observer는 기본적으로 브라우저 뷰포트(Viewport)와 설정한 요소(Element)의 교차점을 관찰하며, 요소가 뷰포트에 포함되는지 포함되지 않는지, 더 쉽게는 사용자 화면에 지금 보이는 요소인지 아닌지를 구별하는 기능을 제공한다.
위에서 intersection observer는 target element가 화면에 노출되었는지 여부를 간단하게 구독할 수 있는 API라고 정리했다.
만약 intersection observer를 사용하지 않고 기능을 구현한다면, scroll이 일어날 때 마다 특정 element가 화면에 존재하는지에 대한 여부를 계속 계산하는 코드를 만들어 줘야할 것이다.
intersection observer를 생성하기 위해서 생성자 호출 시 콜백 함수를 제공해야한다. 이 콜백 함수는 threhold가 한방향 혹은 다른 방향으로 교차 할 때 실행된다.
threhold 란?
- default: 0
- 0.0부터 1.0 사이의 숫자 혹은 이 숫자들로 이루어진 배열로, 타겟 엘리먼트에 대한 교차 영역 비율을 의미한다. 0.0의 경우 타겟 엘리먼트가 교차영역에 진입했을 시점에 observer를 실행하는 것을 의미하고, 1.0의 경우 타켓 엘리먼트 전체가 교차영역에 들어왔을 때 observer를 실행하는 것을 의미한다.
fetchPins를 만들어준다. 무한 스크롤을 구현하기 위해서는 데이터를 처음부터 계속 보여줘야하기 때문에 데이터를 담고 있는 state인 pins 를 operator연산자로 복제해서 축적시켜줬다.page를 만들어준다. 그리고 useEffect로 page의 넘버가 바뀔 때마다 fetchPins 함수를 호출시킨다.아직 백엔드 통신전이라서 데이터를 불러오는 api는 unsplash에서 제공해주는 api를 사용했다. 참고
const Main = () => {
const [pins, setPins] = useState([]);
const [page, setPage] = useState(1); //스크롤이 닿았을 때 새롭게 데이터 페이지를 바꿀 state
const [loading, setLoading] = useState(false); //로딩 성공, 실패를 담을 state
const fetchPins = async page => {
const API_KEY = "API KEY 입력";
const res = await fetch(
`https://api.unsplash.com/photos/?client_id=${API_KEY}&page=${page}&per_page=10`
);
const data = await res.json();
setPins(prev => [...prev, ...data]);
setLoading(true);
};
useEffect(() => {
fetchPins(page);
}, [page]);
...생략
}
fetchPins를 실행시켜 데이터를 불러왔다면, page를 1씩 증가시켜서 그 다음 데이터를 불러오도록 한다. 새롭게 불러와진 데이터는 fetchPins를 통해서 기존에 있던 데이터에 더해지게 된다.loadMore함수를 만들어서 page가 1씩 더해지는 함수를 만든다.const Main = () => {
const loadMore = () => {
setPage(prev => prev + 1);
}
...생략
}
<Loading/> 이기 때문에 여기에 ref를 지정해놓고 탐색 타겟으로 정해 놓는다. 여기에 도달했을 때 데이터 패칭을 시킨다.import { useRef } from "react";
const Main = () => {
const pageEnd = useRef();
...생략
return(
...생략
<Loading ref={pageEnd} />
)
}
Intersection Observer를 통해서 뷰포트 내에 지정해둔 타겟 <Loading/>를 찾고 있으면, loadMore함수를 실행시켜 page를 1씩 증가시키도록 한다.const Main = () => {
...생략
useEffect(() => {
if (loading) {
//로딩되었을 때만 실행
const observer = new IntersectionObserver(
entries => {
if (entries[0].isIntersecting) {
loadMore();
}
},
{ threshold: 1 }
);
//옵져버 탐색 시작
observer.observe(pageEnd.current);
}
}, [loading]);
...생략
}
마무리✨
무한 스크롤 기능을 구현하기 위해서 Intersection Observer를 처음 사용해봤다. 무한 스크롤 기능을 구현하고자 했을 때 처음에는 무작정 라이브러리만 찾았는데 Intersection Observer를 이해하고 나니 라이브러리 없이도 구현할 수 있을 것 같았고 덕분에 이 부분을 더 잘 이해하고 넘어 갈 수 있었던 것 같다.
포스트 잘 봤습니다!
데모 키라 크게 상관 없을 것 같기는 하지만 중간에 api key 노출된 부분은 수정하시는 게 좋지 않을까요?