미니 프로젝트 [MbtiColor] (5)

박기범·2024년 9월 22일
0

이제는 api 통신을 해야할 차례이다.
우선 메인 페이지에 있는 설문 조사들부터 가져오기로 하였다.

요구사항에서는 무한스크롤로 구현하라고 되어있었다.

일단 axios를 이용한 api 함수부터 만들었다.
mbti와 limit, offset을 통해 필터링을 할 수 있으며
mbti가 undefined인 경우에는 전체 데이터가 나왔었다.

limit는 화면에 보여줄 개수로써 깔끔하게 10개로 하도록 했다.

import api from './base';

const getColorSurvey = async (mbti?: string, limit?: number, offset?: number) => {
  try {
    const response = await api.get('/api/color-surveys', {
      params: {
        mbti,
        limit,
        offset,
      },
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching users:', error);
    throw error;
  }
};

export default getColorSurvey;

이렇게 get 요청을 하는 api 함수를 만들었고

header 부분에 필터링을 할 수 있도록 mbti를 선택할 수 있는 기능을 만들었는데 이 데이터를 전역적으로 관리할 필요가 있었다.

따라서

import { create } from 'zustand';

interface FilterMbtiState {
  filterMbti: string;
  setFilterMbti: (mbti: string) => void;
}

// devtools를 사용하는 경우, 타입을 지정해줘야 합니다.
const useFilterMbtiStore = create<FilterMbtiState>((set) => ({
  filterMbti: '',
  setFilterMbti: (mbti: string) => set({ filterMbti: mbti }),
}));

export default useFilterMbtiStore;

저장 공간을 만들었으며 아래와 같이 onChange 함수로 값을 갱신하도록 만들어주었다.

import mbtiTypes from '../constant/mbti';
import useFilterMbtiStore from '../store/filterMbtiStore';

const Header = () => {
  const { filterMbti, setFilterMbti } = useFilterMbtiStore();

  const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
    setFilterMbti(event.target.value);
  };

  return (
 ...
  );
};

export default Header;

이제 무한스크롤을 만들어 줄 차례다. 무한스크롤은 내가 항상 써왔던 방식인 useInfinityQuery와 react-intersection-observer 라이브러리를 사용하기로 정하였다.

import { useEffect } from 'react';
import { useInfiniteQuery } from '@tanstack/react-query';
import { useInView } from 'react-intersection-observer';
import getColorSurvey from '../api/getColorSurvey';
import useFilterMbtiStore from '../store/filterMbtiStore';
import { ColorSurvey, ColorSurveyInfo } from '../types/colorSurvey';

const MbtiColorInfo = () => {
  const { filterMbti } = useFilterMbtiStore();

  const { data, fetchNextPage, hasNextPage, isFetching } = useInfiniteQuery<ColorSurvey>({
    queryKey: ['getColorSurveys', filterMbti],
    queryFn: ({ pageParam = 0 }: any) => getColorSurvey(filterMbti || undefined, 10, pageParam), // offset을 pageParam * 10으로 설정
    getNextPageParam: (lastPage, allPages) => {
      // 현재 페이지 수
      const currentPage = allPages.length;
      // 다음 페이지의 offset 계산 (10씩 더하기)
      return currentPage < lastPage.count / 10 ? currentPage : undefined;
    },
    initialPageParam: 0, // 초기 페이지 파라미터 설정
  });

  const { ref, inView } = useInView({
    threshold: 1.0,
  });

  useEffect(() => {
    if (inView && hasNextPage) {
      fetchNextPage();
    }
  }, [inView, fetchNextPage, hasNextPage]);

  return (
    <div className="flex w-[100%] flex-col items-start self-stretch">
   ...
      <ul className="flex flex-col w-[100%]">
        {data?.pages.map((page) =>
          page.results.map((item: ColorSurveyInfo) => (
          ...
      </ul>
      <div ref={ref} /> {/* 이 div가 뷰포트에 들어오면 다음 페이지를 불러옵니다 */}
    </div>
  );
};

export default MbtiColorInfo;

다음과 같이 쿼리 키를 전역 상태인 filterMbti로 특정 지으면서 바뀔때마다 새로운 데이터가 패칭되도록 해주었다. 또한 offset값을 10씩 더해주는 방식으로 진행하였음.
마지막으로 react-intersection-observer 라이브러리의 useInView 훅과 useEffect를 사용하여 뷰포트에 들어오고 다음 페이지가 있는 경우 10개씩 더 랜더링 해주도록 구현하였다.

profile
프론트엔드 개발공부를 하고있습니다.

0개의 댓글

관련 채용 정보