[JavaScript] 교차점에서 다음 페이지를 불러오는 무한스크롤 기능 추가하기

Moon Hee·2022년 10월 9일
0

트러블 슈팅

목록 보기
22/26
post-thumbnail

마지막 상품이 내가 보고있는 화면에 보이면(보이는 정도를 정할 수 있음) 콜백함수를 실행해서 다음 상품들을 화면에 그려주는 것이 IntersectionObserver를 사용한 무한스크롤의 핵심 아이디어이다.


1. 다음 페이지 불러오는 방식을 정하기 위해 자료구조 확인하기

우선 사용하고 있는 API가 아이템을 얼마나 가져오는지, 다음 아이템들을 가져오려면 어떻게 해야하는지 데이터 자료구조를 보고 확인해야 한다.
나는 next 정보를 활용해서 page를 +1씩 할당한 후 다음 페이지를 불러오기로 했다.

// api.js
import { API_URL } from "./constants";

export default async function getProducts(page) {
    try {
        const res = await fetch(`${API_URL}/products/?page=${page}`, {
            method: "GET",
            headers: {
                "Content-Type": "application/json",
            },
        });
        const data = await res.json();
        window.localStorage.setItem("page", page);
        window.localStorage.setItem(
            "next",
            data.next === null ? data.next : data.next.replace(/\D/g, "")
        );
        return data;
    } catch (e) {
        console.error(e);
    }
}
// console.log(data);
{
	"count": Int,
  	"next": null이나 String,
  	"previous": null이나 String,
  	"results": [
			{
				"product_id": Int,
				"product_name": String,
				"seller": Int,
	      		"seller_store": String,
	      		"image": String,
	      		"price": Int,
	      		"shipping_method": String,
	      		"shipping_fee": Int,
	      		"stock": Int,
	      		"products_info": String,
			}]
}

2. 무한스크롤 함수 만들기

2-1. 감시대상 정하기

let items = document.querySelector(".list-products").childNodes;

2-2. 마지막 상품 찾는 함수 선언하기

let observeLastItem = (observer, items) => {
	let lastItem = items[items.length - 1];
	observer.observe(lastItem); // 마지막아이템 관찰함수 실행(2-3)
 };

2-3. IntersectionObserver 생성자 함수 선언하기

뷰포트 하단에서 마지막 아이템이 options의 조건에 부합할 때 callback을 실행하도록 한다.

let observer = new IntersectionObserver(callback, options);

2-4. 마지막 아이템 찾는 함수 실행하기

observeLastItem(observer, items);

3. 전체코드

import { ProductCard } from "../components/productCard";
import getProducts from "./api";

export default function infiniteScroll() {
    let items = document.querySelector(".list-products").childNodes; // 감시대상노드
    let callback = (entries, observer) => {
        entries.forEach((entry) => {
            if (entry.isIntersecting) {
                let page = parseInt(window.localStorage.getItem("page"));
                let next = window.localStorage.getItem("next");
                if (next === "null") {
                    return;
                } else if (parseInt(next) === page + 1) {
                    getProducts(page + 1).then((data) => {
                        const productList =
                            document.querySelector(".list-products");
                        data?.results.forEach((item) => {
                            const productItem = document.createElement("li");
                            const productCard = new ProductCard(item);
                            productItem.appendChild(productCard.render());
                            productList.appendChild(productItem);
                        });
                    });
                    observer.unobserve(entry.target); // 1페이지의 lastItem 관찰 그만하기
                    setTimeout(() => {
                        observeLastItem(observer, items);
                    }, 3000);
                }
            }
        });
    };

    const options = { threshold: 0.5 };

    // 마지막아이템 찾는 함수 선언
    let observeLastItem = (observer, items) => {
        let lastItem = items[items.length - 1];
        observer.observe(lastItem); // 마지막아이템 관찰함수 실행
      	lastItem.style.background = "pink"; // 마지막 아이템 표시해서 확인
    };

    // observer 생성자 함수 선언
    let observer = new IntersectionObserver(callback, options);

    // 마지막아이템 찾는 함수 실행
    observeLastItem(observer, items);
}
profile
프론트엔드 개발하는 사람

0개의 댓글