알고는 가자

BackEnd_Ash.log·2024년 12월 5일
0

universe

목록 보기
1/5
import { Table, TableBody, TableContainer, TableHead } from '@/styles/pages/board/BoardPage.styled';
import React, { useEffect, useRef, useState } from 'react';
import CompanyList from '@/components/Company/CompanyList/CompanyList';
import { useRouter } from 'next/router';
import { findCompanyListApplication } from '@/api/company.api';
import throttle from 'lodash/throttle';

export interface CompanyData {
  id: number;
  companyName: string;
  userName: string;
  createdAt: string;
}

const Index = () => {
  const router = useRouter();
  const [companyData, setCompanyData] = useState<CompanyData[]>([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const [isLoading, setIsLoading] = useState(false); // 데이터 로드 상태 추가
  const observerRef = useRef<HTMLDivElement | null>(null);

  const moveToDetailCompanyPage = (id: number) => {
    router.push(`/company/${id}`);
  };

  const fetchCompanyData = async (page: number) => {
    if (isLoading) return; // 이미 로드 중이라면 실행 방지
    setIsLoading(true);
    try {
      const response = await findCompanyListApplication(page, 10);
      if (response.length > 0) {
        setCompanyData((prev) => [...prev, ...response]);
      } else {
        setHasMore(false);
      }
    } catch (error) {
      console.error('Failed to fetch company data:', error);
    } finally {
      setIsLoading(false); // 로드 상태 해제
    }
  };

  const throttledFetch = throttle(() => {
    if (!isLoading && hasMore) {
      setPage((prevPage) => prevPage + 1);
    }
  }, 1000); // 1초 간격으로 실행 제한

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting && hasMore) {
          throttledFetch(); // 스로틀링된 함수 호출
        }
      },
      { threshold: 1.0 }
    );

    if (observerRef.current) {
      observer.observe(observerRef.current);
    }

    return () => {
      if (observerRef.current) {
        observer.unobserve(observerRef.current);
      }
    };
  }, [hasMore, isLoading]); // isLoading 상태 추가

  useEffect(() => {
    if (hasMore && !isLoading) {
      fetchCompanyData(page);
    }
  }, [page]); // 페이지 변경 시 호출

  return (
    <TableContainer>
      <Table>
        <TableHead>
          <tr>
            <th>번호</th>
            <th>회사이름</th>
            <th>글쓴이</th>
            <th>날짜</th>
          </tr>
        </TableHead>
        <TableBody>
          <CompanyList companyData={companyData} moveToDetailPage={moveToDetailCompanyPage} />
        </TableBody>
      </Table>
      {hasMore && <div className="observer" ref={observerRef}></div>}
      <style jsx>
        {`
					.observer {
						height: 20px;
						background-color: transparent;
					}
        `}
      </style>
    </TableContainer>
  );
};

export default Index;

위의 코드를 적용하게 되면 중복해서 데이터가 나왔다.
백엔드 response 도 잘못 되지 않았다.

import { Table, TableBody, TableContainer, TableHead } from '@/styles/pages/board/BoardPage.styled';
import React, { useEffect, useRef, useState } from 'react';
import CompanyList from '@/components/Company/CompanyList/CompanyList';
import { useRouter } from 'next/router';
import { findCompanyListApplication } from '@/api/company.api';
import throttle from 'lodash/throttle';

export interface CompanyData {
  id: number;
  companyName: string;
  userName: string;
  createdAt: string;
}

const Index = () => {
  const router = useRouter();
  const [companyData, setCompanyData] = useState<CompanyData[]>([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const [isLoading, setIsLoading] = useState(false); // 로딩 상태 추가
  const observerRef = useRef<HTMLDivElement | null>(null);

  const moveToDetailCompanyPage = (id: number) => {
    router.push(`/company/${id}`);
  };

  const fetchCompanyData = async (page: number) => {
    if (isLoading) return; // 이미 로드 중이면 실행 방지
    setIsLoading(true);
    try {
      const response = await findCompanyListApplication(page, 10);
      if (response.length > 0) {
        setCompanyData((prev) => {
          const merged = [...prev, ...response];
          const unique = merged.filter(
            (item, index, self) =>
              index === self.findIndex((t) => t.id === item.id) // 중복 ID 제거
          );
          return unique;
        });
      } else {
        setHasMore(false);
      }
    } catch (error) {
      console.error('Failed to fetch company data:', error);
    } finally {
      setIsLoading(false);
    }
  };

  const throttledFetch = throttle(() => {
    if (!isLoading && hasMore) {
      setPage((prevPage) => prevPage + 1);
    }
  }, 1000); // 1초 간격으로 실행 제한

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting && hasMore && !isLoading) {
          throttledFetch(); // 스로틀링된 함수 호출
        }
      },
      { threshold: 1.0 }
    );

    if (observerRef.current) {
      observer.observe(observerRef.current);
    }

    return () => {
      if (observerRef.current) {
        observer.unobserve(observerRef.current);
      }
    };
  }, [hasMore, isLoading]); // isLoading 상태 추가

  useEffect(() => {
    if (hasMore && !isLoading) {
      fetchCompanyData(page);
    }
  }, [page]); // 페이지 변경 시 호출

  return (
    <TableContainer>
      <Table>
        <TableHead>
          <tr>
            <th>번호</th>
            <th>회사이름</th>
            <th>글쓴이</th>
            <th>날짜</th>
          </tr>
        </TableHead>
        <TableBody>
          <CompanyList companyData={companyData} moveToDetailPage={moveToDetailCompanyPage} />
        </TableBody>
      </Table>
      {hasMore && <div className="observer" ref={observerRef}></div>}
      <style jsx>
        {`
					.observer {
						height: 20px;
						background-color: transparent;
					}
        `}
      </style>
    </TableContainer>
  );
};

export default Index;

다른점은

중복 데이터 필터링
중복 데이터를 제거하기 위해 setCompanyData를 업데이트할 때, 중복된 항목을 제거하는 로직을 추가

위의 코드는 효율적이지 않을수 있다.

const merged = [...prev, ...response];
const unique = merged.filter(
  (item, index, self) => index === self.findIndex((t) => t.id === item.id) // 중복 ID 제거
);
return unique;

이 방식은 API 에서 반환된 데이터와 기존 데이터( prev ) 를 먼저 병합한 후, 중복된 데이터를 제거하는 방식이다.
이 접근법은 이미 불필요한 데이터가 클라이언트로 내려온 이후에야 중복을 제거하기 때문에 효율적이지 않다.

간단한 방법으로는

module.exports = {
  reactStrictMode: false,
};

변경하니까 중복 호출을 막았다.

profile
꾸준함이란 ... ?

0개의 댓글