๋ฐ˜์‘ํ˜•

๐Ÿ”ฐ React์—์„œ ๋ฌดํ•œ ์Šคํฌ๋กค์„ ์ดํ•ดํ•˜๊ณ  ๋งŒ๋“ค ์ˆ˜์žˆ๋‹ค.

 

โœ… ๋ฌดํ•œ ์Šคํฌ๋กค

์›น์‚ฌ์ดํŠธ์—์„œ ์‚ฌ์šฉ์ž๋“ค์—๊ฒŒ ํŽธ๋ฆฌํ•œ ๊ธ€๋ชฉ๋ก์„ ์ œ๊ณตํ•˜๋ ค๋Š” ๊ธฐ์กด์—๋Š” ํŽ˜์ด์ง€๋„ค์ด์…˜์„ ์ด์šฉํ•˜์—ฌ ์ œ๊ณตํ•˜์˜€๋‹ค.

ํ•˜์ง€๋งŒ ํŽ˜์ด์ง€๋„ค์ด์…˜์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค์ŒํŽ˜์ด์ง€๋กœ ๋„˜์–ด๊ฐ€๋Š” ๋ฒˆํ˜ธ๋ฅผ ๋ˆŒ๋Ÿฌ์ค˜์•ผํ•œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

 

์‚ฌ์šฉ์ž๋“ค์€ ๋œ ๊ธฐ๋‹ค๋ฆฌ๊ณ , ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์›น์„œํ•‘์„ ํ•˜๊ณ  ์‹ถ์–ดํ•œ๋‹ค.

์ด๋Ÿฌํ•œ ๋‹จ์ ์„ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•ด ๋‚˜์˜จ ๊ฒŒ ๋ฌดํ•œ ์Šคํฌ๋กค(infinite scroll)์ด๋‹ค.

 

 

โœ… ์†Œ์Šค ์ฝ”๋“œ

 

์ „์ฒด ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๊ณ  ์•„๋ž˜์—์„œ ์ข€ ๋” ์ž์„ธํžˆ ์„ค๋ช…์„ ๋•๊ณ ์ž ํ•œ๋‹ค.

"use strict";

import { useEffect, useState } from "react";

function App() {
  const randomData = [];
  const [pageCount, setPageCount] = useState(0);
  const [fetching, setFetching] = useState(false); // ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ fetchํ•˜๋Š” ์ค‘์ธ์ง€ ์•„๋‹Œ์ง€ ํ™•์ธํ•˜๋Š” ์—ญํ• 
  const [myData, setMyData] = useState([]); // API๋กœ๋ถ€ํ„ฐ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ณณ

  const createRandomData = () => {
    for (let i = 1; i <= 30; i++) {
      const data = {
        title: `title ${i}`,
        content: Math.random(),
      };

      randomData.push(data);
    }
  };

  createRandomData();

  const handleScroll = () => {
    const scrollHeight = document.documentElement.scrollHeight;
    const scrollTop = document.documentElement.scrollTop;
    const clientHeight = document.documentElement.clientHeight;

    // ํŽ˜์ด์ง€ ๋์— ๋„๋‹ฌํ•˜๊ณ  fetch๊ฐ€ ์ง„ํ–‰๋˜์ง€ ์•Š๋Š” ์ƒํƒœ์ผ ๋•Œ ๋™์ž‘
    if (fetching === false && scrollTop + clientHeight >= scrollHeight) {
      fetchMoreData();
    }
  };

  // scroll event listener ๋“ฑ๋ก ๋ฐ ํ•ด์ œ
  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }]);

  const fetchMoreData = async () => {
    // ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ fetchํ•˜๋Š” ์ƒํƒœ๋กœ ์ „ํ™˜
    setFetching(true);

    if (pageCount >= randomData.length) {
      return;
    }

    const fetchedData = randomData[pageCount];
    const mergedData = myData.concat(fetchedData);

    setMyData(mergedData);
    setPageCount(pageCount + 1);

    setFetching(false);
  };

  return (
    <div className="App">
      {randomData.map((elem, index) => (
        <div key={index}>
          <h2>์ผ๋ฐ˜</h2>
          <div>{elem.title}</div>
          <div>{elem.content}</div>
        </div>
      ))}
      {myData.map((elem, index) => (
        <div key={index}>
          <h2>์ƒˆ๋กœ</h2>
          <div>{elem.title}</div>
          <div>{elem.content}</div>
        </div>
      ))}
    </div>
  );
}

export default App;

 

 


 

์šฐ์„  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด ๋‚ด๊ธฐ ์œ„ํ•ด randomData๋ฅผ ์–ป์–ด๋‚ธ๋‹ค.

์ด ๊ณผ์ •์ด ์ถ”ํ›„์—๋Š” api ํ˜ธ์ถœ์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š”์‹์œผ๋กœ ๋ณ€๊ฒฝ๋˜๋ฉด ๋˜๊ฒ ๋‹ค.

  const createRandomData = () => {
    for (let i = 1; i <= 30; i++) {
      const data = {
        title: `title ${i}`,
        content: Math.random(),
      };

      randomData.push(data);
    }
  };

  createRandomData();

 

useEffect๋ฅผ ํ†ตํ•ด scroll ์ด๋ฒคํŠธ๋ฅผ ๋“ฑ๋กํ•ด์ค€๋‹ค.

์ด๋•Œ handleScroll์—์„œ ์Šคํฌ๋กค์ด ๊ฐ€์žฅ ํ•˜๋‹จ์— ๋„์ฐฉํ–ˆ์„ ๋•Œ fetchMoreData๋ฅผ ํ˜ธ์ถœํ•ด์ค€๋‹ค.

๋˜ํ•œ fetching === false๋ฅผ ํ†ตํ•ด fetch์ค‘์— ๊ณ„์† ์Šคํฌ๋กค์„ ๊ฐ€์žฅ ํ•˜๋‹จ์œผ๋กœ ์˜ฎ๊ฒผ์„ ๋•Œ ์ค‘๋ณตํ˜ธ์ถœ์ด ๋˜์ง€ ์•Š๋„๋ก ํ•œ๋‹ค.

  const handleScroll = () => {
    const scrollHeight = document.documentElement.scrollHeight;
    const scrollTop = document.documentElement.scrollTop;
    const clientHeight = document.documentElement.clientHeight;

    // ํŽ˜์ด์ง€ ๋์— ๋„๋‹ฌํ•˜๊ณ  fetch๊ฐ€ ์ง„ํ–‰๋˜์ง€ ์•Š๋Š” ์ƒํƒœ์ผ ๋•Œ ๋™์ž‘
    if (fetching === false && scrollTop + clientHeight >= scrollHeight) {
      fetchMoreData();
    }
  };

  // scroll event listener ๋“ฑ๋ก ๋ฐ ํ•ด์ œ
  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  });

 

fetchMoreData์—์„œ๋Š” setFetching์„ ํ†ตํ•ด ๋งˆ์น˜ mutex๋ฅผ ์“ฐ๋“ฏ์ด ์ž‘์—… ๋™๊ธฐํ™”๋ฅผ ๊ฑธ์–ด์ค€๋‹ค.

๊ทธ๋ฆฌ๊ณ  setMyData๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค. 

  const fetchMoreData = async () => {
    // ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ fetchํ•˜๋Š” ์ƒํƒœ๋กœ ์ „ํ™˜
    setFetching(true);

    if (pageCount >= randomData.length) {
      return;
    }

    const fetchedData = randomData[pageCount];
    const mergedData = myData.concat(fetchedData);

    setMyData(mergedData);
    setPageCount(pageCount + 1);

    setFetching(false);
  };
๋ฐ˜์‘ํ˜•