안녕하세요. 오늘은 recoil을 이용한 무한스크롤 방식에서 Tanstack-query를 이용한 무한스크롤 방식으로 변경한 이유와 방법에 대해
공유하려고합니다. 부족한 내용이지만 기록을 해보겠습니다.
Recoil에서 사용한 무한스크롤 방법
Recoil에서 사용한 무한스크롤방법은 다음과 같습니다.
1. 데이터 패칭 - 한번에 관련된 모든 데이터를 불러온다.
2. recoil 캐싱 - 불러온 데이터를 recoil을 이용해서 캐싱처리한다. (SelectorFamily)
3. selector를 이용해서 데이터 slice - 무한스크롤은 fetching Event가 발생할때마다 새로운 데이터를 보여주는 방식이다.
그래서 한번에 받아온 데이터를 한번에 보여주는 것이 아닌, slice를 이용해서 일정부분씩 보여주는 Filter를 사용하였다.
Recoil을 이용해서 구현한 무한 스크롤 로직
const useInfiniteScroll = ({ initData }: Props) => {
const [dataSource, setdataSource] = useState<InfiniteScrollData[]>([]);
const [hasMore, sethasMore] = useState(true);
const setCount = useSetRecoilState(galleryScrollCountState);
const [newPage] = useRecoilValue(waitForAll([filteredPageState]));
useEffect(() => {
if (initData) {
setdataSource(initData);
sethasMore(true);
setCount(0);
} else {
sethasMore(false);
}
}, [initData]);
const fetchMoreData = async () => {
if (newPage.slicedData.length !== 0) {
try {
setTimeout(() => {
const list = newPage.slicedData;
if (list) {
const newList = dataSource.concat(list);
setdataSource(newList);
setCount(prev => prev + 1);
} else {
console.log('error');
}
}, 1500);
} catch (error) {
console.error('데이터를 가져오는 중 오류가 발생했습니다:', error);
}
} else {
sethasMore(false);
}
};
return {
fetchMoreData,
dataSource,
hasMore,
};
};
export default useInfiniteScroll;
export const filteredPageState = selector({
key: 'InfinitedScrollState',
get: ({ get }) => {
const galleryList = get(galleryInfoQuery(get(searchParams)));
const page = get(galleryScrollCountState);
const itemCount = get(itemsPerPage);
if (axios.isAxiosError(galleryList)) {
return {
slicedData: [],
startIndex: 0,
endIndex: 0,
state: galleryList,
};
}
if (galleryList === null || galleryList.length <= itemCount) {
return {
slicedData: [],
startIndex: 0,
endIndex: 0,
state: galleryList,
};
}
const startIndex = (page + 1) * itemCount;
const endIndex = startIndex + itemCount;
const galleryInit = galleryList.slice(0, itemCount);
const slicedData = galleryList.slice(startIndex, endIndex);
return { galleryInit, slicedData, startIndex, endIndex };
},
});
export const galleryInfoQuery = selectorFamily({
key: 'GallerySelectorFamily',
get: (params: SearchParams) => async () => {
const response = await getGallery(params);
return response;
},
});
Recoil에서 Tanstack-query 변경 이유
다음과 같이 구현을 했던 이유는 당시 개발할때는 시간이 부족해서, BE측에서 데이터를 나눠서 보내줄 수 없었습니다.
그렇기에 FE측에서 데이터를 분할해서 사용하기로 했고 큰 문제는 없었습니다.
하지만 현재는 문제가 없겠지만, 데이터 양이 많이질 수 록 문제가 발생할 수 있는 부분이라 생각했습니다.
1. 성능 문제: 한 번에 많은 양의 데이터를 불러와서 캐싱하면 초기 로딩 시간이 길어질 수 있습니다. 또한 캐싱된 데이터가 많아지면 메모리 사용량이 증가하여 성능 문제를 야기할 수 있습니다.
2. 리소스 소비: 모든 데이터를 미리 불러와서 캐싱하면 서버 및 클라이언트의 리소스를 과도하게 소비할 수 있습니다. 이는 서버 부하와 네트워크 트래픽 증가로 이어질 수 있습니다.
3. 네트워크 트래픽 감소: 조금씩 데이터를 가져오는 방식은 한 번에 많은 양의 데이터를 요청하는 것보다 네트워크 트래픽을 줄일 수 있습니다. 이는 사용자가 이동 중이거나 네트워크 연결이 느린 경우에도 페이지를 빠르게 로드할 수 있도록 도와줍니다.
4. 코드의 복잡성 : Tanstack Query를 사용하여 무한 스크롤 기능을 구현할 때 코드의 복잡성이 줄어듭니다. useInfiniteQuery를 사용하면 간편하게 무한 스크롤을 구현할 수 있으며, 적은 양의 코드로 원하는 기능을 구현할 수 있습니다. 반면에 Recoil을 사용하여 무한 스크롤을 구현하려면 더 많은 코드가 필요합니다. 데이터의 상태를 관리하고 이를 기반으로 무한 스크롤 동작을 제어해야 하기 때문에 코드가 더 복잡해질 수 있습니다. 따라서 Tanstack Query를 사용하는 것이 코드의 복잡성을 줄이고 무한 스크롤 기능을 더욱 간편하게 구현할 수 있는 장점이 있습니다.
구현 로직 (Tanstack-query - useInfiniteQuery)
구현하기
기능적 내용 - UseInfiniteGalley Hooks
input : max_page_per_count, filter
output : data.pages, data.pageParams ...
기능 정리
input | max_page_per_count | number | 페이지당 item 갯수 |
filter | select : number type : string category : string |
각 항목별 필터 | |
output | data | pages:TData[] | 모든 페이지를 포함하는 배열 (페이지 마다 아이템 리스트 배열) |
pageParams | 모든 페이지 매개변수를 포함하는 배열 | ||
isLoading | boolean | 데이터 처음 캐싱처리가 완료 됬는지 | |
fetchNextPage | (option? :FetchNextPageOptions) => Promise<UseInfiniteQueryResult) | 다음 페이지 정보 불러오기 | |
hasNextpage | boolean | 다음 페이지가 있는지 정보 | |
기능적 내용 - getInfiniteGallery (http 메서드 GET)
input | pageParams | number | 페이지 index (현재 페이지 순서) |
filter | select : number type : string category : string |
각 항목별 필터 | |
size | number | 페이지 당 item 갯수 | |
output | result | GalleyItem[] | 갤러리 아이템 정보 |
error | error | 에러 정보 |
주고 받을 데이터 양식
{
"title": "테스트입니다.",
"url": "https://626386e1-b34a-4f72-a682-2469833474b1",
"memberUrl": "https://profile.png",
"memberId": 7,
"author": "홍길동",
"id": 17,
"isLike": false,
"likeNum": 0
},
{
"title": "이불 속이 안좋아",
"url": "https://626386e1-b34a-4f72-a682-2469833474b1",
"memberUrl": "https://profile.png",
"memberId": 2,
"author": "이순신",
"id": 7,
"isLike": true,
"likeNum": 4
},
Tanstack-query useInfiniteQuery 사용시에 겪었던 점
1. queryFn에 사용할 http 요청 함수에는 pageParam이 꼭 있어야한다. (버전확인도 잘하기)
공식 홈페이지를 확인하면 나오긴 하지만 잘봐야한다.
TanStack query를 사용한다고 하면 꼭 버전확인을 잘해야된다. 공식문서를 보고 완성한다고 하면 문제는 없겠지만,
블로그같은 다른사람의 글을 보고 작성한다고 했을 때 문제가 발생할 수도 있다.
TanStack query v4 -> v5로 가면서 변경된 내용이 있다.
initialPageParam: TPageParam
Required
initialPageParam이 이제 필수값으로 적용됬다. 만약에 이거를 빠트리게 된다면 문제가 발생할 수도 있으니 주의하자.
2. infiniteQuery를 사용할 때 pageParams가 꼭 필요하다.
useInfiniteQuery는 QueryFunctionContext를 입력값으로 받는데, QueryFunctionContext는 infiniteQuery를 사용할 때 pageParam을 매개변수로 받는다. 이건 꼭 주의해줘야된다.
정리
무한스크롤 구현을 여러 방식을 걸쳐서 정리한거같다. 다양한 방법이 있지만 더 효율적이고 코드를 간결하게 할 수 있도록 변경해가는것은 좋은거같다. 팀웍크를 위해서도 코드는 최대한 간결하고 가독성있게 변경해 가도록 해야겠다.
코드와 예시영상은 추후 올릴 예정이다.
'문제 해결하기 - FE' 카테고리의 다른 글
react-hook-form 컴포넌트 의존성 관리하기 (0) | 2024.04.05 |
---|---|
CRA(Craco)에서 Vite로 전환기 - 1 (0) | 2024.03.19 |
TanStack query InfiniteQuery 사용 중 발생한 캐싱 문제 (0) | 2024.03.07 |
husky + lint-staged가 필요한 이유 (prettier + eslint) (1) | 2024.03.06 |
Recoil / Tanstack query 역할 분리하기 (0) | 2024.03.05 |