무한 스크롤과 Intersection Observer API
의 특징에 대해 알아봅시다.
무한 스크롤과 페이지네이션
무한 스크롤 : 스크롤을 무한으로 할 수 있는 기능이다. 즉, 페이지 최하단에 도달했을 때 신규 콘텐츠를 로드하는 식으로 무한 스크롤이 구성된다.
- ex. instagram, Pinterest
무한 스크롤 이전에는 페이지네이션을 활용해 콘텐츠를 확인하는 방식을 사용했다.
무한스크롤의 장점
- 별도의 추가 동작이 필요하지 않음.
- 몰입도 증가
무한스크롤의 단점
- 페이지 성능 저하
- 많은 콘텐츠를 로드하면 브라우저 메모리 사용량이 증가해 페이지 성능이 느려질 수 있다.
- 특히 사용자의 디바이스 성능에 의존된다.
- 콘텐츠 탐색 어려움
- 사용자가 스크롤을 내리다가 "눈여겨봤던 콘텐츠"를 다시 찾으려고 하면 어려울 수 있다.
- 고정된 위치나 URL 없이 계속 데이터를 로드하므로 특정 콘텐츠를 기억하고 다시 접근하기 힘들다.
- SEO(검색 엔진 최적화) 문제
- 검색 엔진이 전체 콘텐츠를 크롤링하기 어려울 수 있어, SEO에 부정적인 영향을 미칠 가능성이 있다.
- 끝이 없다는 피로감
- 사용자가 콘텐츠의 끝을 알 수 없어서 피로감을 느끼거나 지루해질 수 있다.
무한 스크롤을 보완하는 방법
- Lazy Loading
- 필요할 때만 데이터를 로드해 성능 문제를 완화한다.
- vs. 일반 로딩 : 모든 이미지를 한 번에 로드한다. 페이지 로드가 느려질 수 있다.
- Back-to-Top 버튼 제공
- 사용자가 한 번에 페이지 상단으로 돌아갈 수 있게 한다.
- 가상 스크롤(Virtual Scrolling)
- 사용자가 보고 있는 콘텐츠만 DOM에 렌더링하여 메모리 사용량을 줄입니다.
- 히스토리 관리
- 스크롤 위치나 특정 콘텐츠를 URL로 저장해 다시 접근할 수 있도록 한한다.
Intersection Oberserver API (MDN)
Intersection Observer API는 상위 요소 또는 최상위 문서의 viewport와 대상 요소 사이의 변화를 비동기적으로 관찰할 수 있는 수단을 제공합니다.
Intersection Oberserver가 필요한 이유
- 페이지가 스크롤 될 때 이미지의 또는 다른 컨텐츠의 지연 로딩(Lazy-loading) 됩니다.
- 스크롤할 때 더 많은 컨텐츠가 로드되고 렌더링 되는 "무한 스크롤" 웹 사이트를 구현함으로써, 사용자가 페이지를 넘길 필요가 없습니다.
- 광고 수익 산정을 위해 광고 가시성을 보고합니다.
- 사용자가 결과를 볼 수 있을지 여부에 따라 작업 또는 애니메이션 프로세스를 수행할지 여부를 결정합니다.
따라서 해당 API를 활용하여 무한 스크롤을 구현할 수 있다.
Intersection Observer 의 구현
Intersection Oberserver(교차 관찰자)는 요소(Element)가 뷰포트(ViewPort)와 교차하고 있는 지 감지한다. 즉, 관찰 중인 요소가 사용자가 보는 화면 영역 내에 들어왔는 지 알려준다.
약 N% 정도 겹친다면 어떤 작업(callback)을 수행해야 한다
직접확인하기
Intersection Observer PlayGround
1. 관찰자 객체 생성
let options = {
root: document.querySelector("#scrollArea"), // 감지 영역
rootMargin: "50px 0px", // 감지 영역을 바깥 범위까지 확장
threshold: 1.0, // 100% 보이면 콜백이 호출된다는 의미
};
// 관찰자
let observer = new Intersection Observer(callback, options)
객체 생성 시 root(관찰 대상이 화면에 들어왔음을 감지하는 영역), rootMargin(감지 영역을 바깥 범위까지 확장), threshold(관찰 대상이 화면 내 얼마나 들어왔을 때 콜백 함수를 콜백할 지) 등의 값을 작성하면 된다.
위 코드에서는 뷰포트 상단을 50px만큼 확장했으므로, 요소가 화면에 완전히 보이기 50px 전에 감지
![[Pasted image 20241118163618.png]]
2. 관찰할 요소 대상으로 하기
관찰자(observer)가 관찰할 타겟 요소를 전달
let target = document.querySelector("#listItem");
observer.observe(target);
// observer를 위해 설정한 콜백은 바로 지금 최초로 실행됩니다
// 대상을 관찰자에 할당할 때까지 기다립니다. (타겟이 현재 보이지 않더라도)
3. 교차 계산 방법
let callback = (entries, observer) => {
entries.forEach((entry) => {
let box = entry.target;
let visiblePct = `${Math.floor(entry.intersectionRatio * 100)}%`;
box.querySelector('.topLeft').innerHTML = visiblePct;
box.querySelector('.topRight').innerHTML = visiblePct;
box.querySelector('.bottomLeft').innerHTML = visiblePct;
box.querySelector('.bottomRight').innerHTML = visiblePct;
});
};
- entry.boundingClientRect : 관찰 대상의 사각형 정보( DOMReactReadOnly)
- entry.intersectionRatio : 관찰 대상의 교차한 영역 정보(DOMReactReadOnly)
- entry.intersectionRect : 관찰 대상의 교차한 영역 백분율 (intersectionRect ~ boundingClintRect영역까지 비율, number)
- entry.isIntersecting : 관찰 대상의 교차 상태 (Boolean)
- entry.rootBounds : 지정한 루트 요소의 사각형 정보(DOMReactReadOnly)
- entry.target ; 관찰 대상 요소 (Element)
- entry.time : 변경이 발생한 시간 정보 (DOMHighResTimeStamp)
무한스크롤 구현
아래와 같은 무한 스크롤을 구현해보자
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>Document</title>
<style>
#content .item {
padding: 10px;
margin: 5px;
background-color: #f0f0f0;
border: 1px solid #ddd;
}
#loader {
text-align: center;
padding: 20px;
background-color: #ff1b1b;
}
</style>
</head>
<body>
<div id="content">
<!-- 동적으로 추가될 콘텐츠 -->
</div>
<div id="loader">Loading...</div>
<script>
// 감지 옵션 설정
let options = {
root: null, // 뷰포트(화면 전체)가 감지 영역
rootMargin: '0px', // 추가 여백 없이 감지
threshold: 1.0, // 100% 보였을 때만 감지
};
// 데이터 로드 함수
function loadMoreData() {
const content = document.getElementById('content');
// 임의로 데이터 추가
for (let i = 0; i < 50; i++) {
const item = document.createElement('div');
item.textContent = `Item ${content.children.length + 1}`;
item.className = 'item';
content.appendChild(item);
}
}
// 감지 대상 요소가 보일 때 호출될 콜백
let callback = (entries, observer) => {
entries.forEach((entry, index) => {
if (entry.isIntersecting) {
console.log(`추가`);
console.log(entry.intersectionRect);
loadMoreData(); // 데이터 로드
}
});
};
// 관찰자 생성
let observer = new IntersectionObserver(callback, options);
let target = document.getElementById('loader');
observer.observe(target); // loader를 감지 대상으로 설정
</script>
</body>
</html>
참고자료
728x90
'React당' 카테고리의 다른 글
React Router의 동적 라우팅(Dynamic Routing)이란 뭘까? (0) | 2024.11.04 |
---|---|
[React] Context API를 위한 useContext hook (2) | 2024.10.07 |
간단히 이해하는 Redux 예제 (0) | 2024.04.16 |
React - 스파게티코드 지양을 위한 Component 구성 방법 (0) | 2024.02.17 |