본문 바로가기

React

[React] infinite scroll(무한 스크롤) 구현 - scroll event

오늘은 다양한 서비스에서 리스트를 보여줄 때 흔히 사용되는 무한 스크롤을 구현해보도록 하겠습니다. 

무한스크롤은 불러올 데이터가 많을 때, 서비스에 부담이 되지 않을만큼 먼저 불러오고 스크롤을 하단으로 내릴때마다 추가로 불러와서 출력해주는 기능을 의미합니다. 

 

먼저 예제코드를 보시겠습니다.

 

 

1. 예제 코드

예제코드에서는 총 표시할 아이템의 수를 입력하면 스크롤을 하단으로 내릴 때마다 5개씩 끊어서 보여주는 코드 입니다.

Home.js

import styles from '@/styles/Home.module.css'
import React, { useEffect, useState } from 'react'


export default function Home() {
  const [input, setInput] = useState(0)
  const [list, setList] = useState([]);
  const [showMore, setShowMore] = useState(false)
  const [page, setPage] = useState(0)
  const [isLast, setIsLast] = useState(false)
  
  const perPage = 5
  
  const onInputChange= (e)=>{
    setList([])
    setPage(0)
    setIsLast(false)
    setShowMore(true)
    setInput(e.target.value)
  }

  useEffect(() => {
    
    const handleScroller = () => {
      const { scrollTop, offsetHeight } = document.documentElement

      //스크롤이 바닥보다 100px 위에 위치하면 이벤트 실행
      if (offsetHeight- (window.innerHeight + scrollTop) < 100) {       
        setShowMore(true)
      }
    }    
    window.addEventListener('scroll', handleScroller)
    return () => window.removeEventListener('scroll', handleScroller)
  }, [])

  useEffect(() => {
    if (showMore) addList()
    else if (isLast) setShowMore(false)
  }, [showMore])

  let addList = () =>{

    if(!isLast){
      let result = []
      let start = page*perPage
      for(let i = start; i < start+perPage; i++) {
        if(i < input){
          result.push(`${i+1}번`)
        }else{
          setIsLast(true)
          break;
        } 
      }
      setPage(prev=>prev+1)
      setList(list.concat(result)) 
      setShowMore(false)  
    }
  }

  return (
    <main >
        <div>
          몇개를 생성할까요?
        </div>
        <input onChange={onInputChange} defaultValue={input}/>
            <div>
              <p>==================================</p>              
              {
                input == 0 ? (null):(
                  // <HorizontalScroll length={input}>

                  // </HorizontalScroll>
                  
                    list.map((item, index) => (
                      <div className={styles.item} key={index}
                        onClick={() => {
                          console.log(`${index}번 클릭입니다 created by ${input}`)
                        }}>
                        <p>{item}</p>
                      </div>
                    ))
                  
                )
              }
        </div>
      </main>    
  )
}

Home.module.css

.item{  
  display: flex;
  justify-content: center;
  align-items: center;
  margin-bottom: 10px;
  flex-shrink: 0;
  width: 200px;
  height: 100px;
  margin-right: 5px;
  background-color: aquamarine;
}

 

 

2. 핵심 내용 설명

 

  let addList = () =>{
    if(!isLast){
      let result = []
      let start = page*perPage
      for(let i = start; i < start+perPage; i++) {
        if(i < input){
          result.push(`${i+1}번`)
        }else{
          setIsLast(true)
          break;
        } 
      }
      setPage(prev=>prev+1)
      setList(list.concat(result)) 
      setShowMore(false)  
    }
  }

 

리스트를 출력해주는 함수입니다. 함수가 출력될때마다 페이지 당 수 만큼 리스트에 추가 해주고 입력받은 전체 계수를 넘어가면 더이상 출력하지 않습니다.

 

  useEffect(() => {
    
    const handleScroller = () => {
      const { scrollTop, offsetHeight } = document.documentElement

      //스크롤이 바닥보다 100px 위에 위치하면 이벤트 실행
      if (offsetHeight- (window.innerHeight + scrollTop) < 100) {       
        setShowMore(true)
      }
    }    
    window.addEventListener('scroll', handleScroller)
    return () => window.removeEventListener('scroll', handleScroller)
  }, [])

 

먼저 스크롤이 내려갔을 때 발생될 이벤트를 정의 해 줍니다. 스크롤이 하단의 바닥으로 내려왔는가를 알아보기 위해 스크롤의 위치(scrollTop), 윈도우의 사이즈(innerHeight), 리스트의 전체 높이(offsetHeight)를 구해서 리스트의 바닥과 윈도우의 바닥의 차이를 구해줍니다. 보다 나은 사용자 경험을 위해 바닥에 오기 전에 리스트를 추가로 출력해줍니다.

 

그리고 만들어준 이벤트 핸들러를 scroll에 달아 줍니다. react 에서는 element에 이벤트 핸들러를 등록 할 때 clean up function에서 remove 까지 해 줘야 합니다. clean up function은 아래 포스팅에 설명 되어 있습니다.

2023.05.29 - [분류 전체보기] - [react] useEffect 완전 사용법 1탄, 예제 코드 - React hook

 

 

3. 실행 화면

 

 

4. 마무리

이상 scroll event를 활용해서 react에서 무한스크롤(infinite scroll)을 구현하는 방법을 알아 보았습니다. 사실 무한스크롤을 구현하는 데에는 scroll event는 그다지 좋은 방법이 아닙니다. 스크롤 이벤트가 너무 많이 발생해서 throttling이든 debouncing 이든 해줘야 앱 성능에 영향을 주지 않습니다. 이런 문제를 해결하기 위해서는 Observer API의 Intersection Observer를 활용하는게 더 나은 선택이 될 것입니다. 그래서 다음 글은 throttling 과 debounce, 그리고 observer API에 대해서 작성해 보도록 하겠습니다.

 

 

질문이나 오류 및 개선사항은 언제든지 댓글 남겨주시기 바랍니다.