무한 스크롤 구현

김수현·2026년 2월 1일

Suhyeon's 개발일지

목록 보기
34/34
post-thumbnail

📌 오늘 완성한 핵심 기능

무한 스크롤 기반 상품 목록 시스템 구현

오늘은 사용자가 스크롤을 내릴 때마다 추가 상품이 자동으로 로드되는 무한 스크롤 기능을 완성했습니다. 단순한 페이지네이션을 넘어서 끊임없는 사용자 경험을 제공하는 모던 웹 애플리케이션의 핵심 기능을 구현했습니다.

🔧 구현한 기능들

1. 전역 상태 관리 시스템

// 무한 스크롤을 위한 상태 변수들
let currentPage = 1;
let currentLimit = 20;
let currentSort = "price_asc";
let isLoading = false;
let hasMoreProducts = true;
  • 현재 페이지, 로딩 상태, 더 로드할 상품 여부 등을 전역으로 관리
  • 여러 함수 간 상태 공유를 통한 일관된 데이터 처리

2. 초기 상품 로드 함수

export async function updateCardProducts(limit, sort) {
  // 상태 초기화
  currentPage = 1;
  currentLimit = limit;
  currentSort = sort;
  hasMoreProducts = true;
  
  // 로딩 상태 표시 후 데이터 로드
}
  • 필터 변경 시 모든 상태를 초기화하고 새로운 조건으로 첫 페이지 로드
  • 기존 상품 목록을 완전히 교체하는 방식

3. 무한 스크롤 추가 로드 함수

export async function loadMoreProducts() {
  if (!hasMoreProducts || isLoading) return;
  
  currentPage += 1;
  
  // 로딩 스켈레톤 추가
  productsGrid.insertAdjacentHTML("beforeend", createLoadingSkeleton(4));
  
  // 새로운 상품 데이터 로드 후 기존 목록에 추가
}

🧠 핵심 학습 내용

1. innerHTML vs insertAdjacentHTML의 차이점

// ❌ innerHTML: 기존 DOM 요소들을 모두 삭제 후 재생성
productsGrid.innerHTML += newHTML; // 이벤트 리스너 소실!

// ✅ insertAdjacentHTML: 기존 요소 유지하며 새 요소만 추가
productsGrid.insertAdjacentHTML("beforeend", newHTML); // 이벤트 리스너 보존!

깨달은 점:

  • 무한 스크롤에서는 반드시 insertAdjacentHTML 사용해야 함
  • 기존 상품들의 이벤트 리스너 보존
  • 성능상 이점: 전체 DOM 재렌더링 방지

2. 상태 관리의 복잡성

// 상품 로드 가능 여부 판단 로직
if ((currentPage - 1) * currentLimit + newProducts.length >= total) {
  hasMoreProducts = false;
}
  • 현재 로드된 상품 수와 전체 상품 수를 비교하여 추가 로드 가능성 판단
  • 페이지 기반 로딩과 총 개수 추적의 동기화

3. 스크롤 감지 이벤트 설정

import { loadMoreProducts } from "../componets/updateProductsCard.js";

export function setupInfiniteScroll() {
  window.addEventListener("scroll", () => {
    // 페이지 하단에 도달했는지 확인
    const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
    const windowHeight = window.innerHeight;
    const documentHeight = document.documentElement.offsetHeight;

    // 하단 200px 전에 미리 로드
    if (scrollTop + windowHeight >= documentHeight - 200) {
      loadMoreProducts();
    }
  });
}

0개의 댓글