무한스크롤을 구현하는데 라이브러리가 존재하긴 했지만, 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 노출된 부분은 수정하시는 게 좋지 않을까요?