Skip to content

Commit be00ae8

Browse files
committed
Refactor: 무한스크롤 리팩토링 #65
1 parent 6a7779f commit be00ae8

File tree

6 files changed

+75
-112
lines changed

6 files changed

+75
-112
lines changed

components/pages/main/Feed/BoardsList/index.tsx

+1-3
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ export default function BoardsList({
2020
data: boardList,
2121
isLoading,
2222
hasNextPage,
23-
} = useGetBoardList({
24-
infiniteQueryKey: ['boards'],
25-
});
23+
} = useGetBoardList();
2624

2725
return (
2826
<FlexBox direction="column" className="w-full gap-10">

hooks/common/useInfiniteScroll.tsx

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { useInfiniteQuery } from '@tanstack/react-query';
2+
import { useEffect } from 'react';
3+
import { useInView } from 'react-intersection-observer';
4+
5+
interface InfiniteScrollProps<T> {
6+
queryKey: string;
7+
firstPageParam: number;
8+
queryFn: (pageNumber: number) => Promise<T>;
9+
getNextPageParamFn: (page: T) => void;
10+
}
11+
12+
export default function useInfiniteScroll<T>({
13+
queryKey,
14+
firstPageParam,
15+
queryFn,
16+
getNextPageParamFn,
17+
}: InfiniteScrollProps<T>) {
18+
const { data, fetchNextPage, isLoading, hasNextPage, isFetchingNextPage } =
19+
useInfiniteQuery({
20+
queryKey: [queryKey],
21+
queryFn: ({ pageParam = firstPageParam }): Promise<T> =>
22+
queryFn(pageParam),
23+
getNextPageParam: getNextPageParamFn,
24+
});
25+
26+
// 무한 스크롤 화면 가장 아래 부분 탐지하는 observer
27+
function Observer({ children }: { children: React.ReactNode }) {
28+
const { ref, inView } = useInView({ threshold: 1 });
29+
useEffect(() => {
30+
if (inView && hasNextPage) {
31+
fetchNextPage();
32+
}
33+
}, [inView]);
34+
if (!hasNextPage || !data) return null;
35+
return (
36+
<div ref={ref}>{isLoading || isFetchingNextPage ? children : null}</div>
37+
);
38+
}
39+
40+
return {
41+
Observer,
42+
data,
43+
isLoading,
44+
hasNextPage,
45+
};
46+
}

hooks/queries/useGetBoardList.tsx

+9-42
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,19 @@
11
'use client';
22

3-
import { useEffect } from 'react';
4-
import { useInView } from 'react-intersection-observer';
5-
import { useInfiniteQuery } from '@tanstack/react-query';
63
import { getBoardList } from '@/service/board';
74
import { BoardList } from '@/types/types';
5+
import useInfiniteScroll from '../common/useInfiniteScroll';
86

9-
interface InfiniteScrollProps {
10-
infiniteQueryKey: string[];
11-
pageParameter?: number;
12-
pageSize?: number;
13-
inViewThreshold?: number;
14-
}
15-
16-
export default function useGetBoardList({
17-
infiniteQueryKey,
18-
pageParameter = 1,
19-
pageSize = 5,
20-
}: InfiniteScrollProps) {
21-
const { data, fetchNextPage, isLoading, hasNextPage, isFetchingNextPage } =
22-
useInfiniteQuery({
23-
queryKey: infiniteQueryKey,
24-
queryFn: ({ pageParam = pageParameter }): Promise<BoardList> =>
25-
getBoardList({ pageParam, pageSize }),
26-
getNextPageParam: (boardlist) =>
27-
boardlist.last ? undefined : boardlist.number + 1,
28-
select: (d) => ({
29-
pages: d.pages.flatMap((page) => page),
30-
pageParams: d.pageParams,
31-
}),
7+
export default function useGetBoardList() {
8+
const { data, isLoading, Observer, hasNextPage } =
9+
useInfiniteScroll<BoardList>({
10+
queryKey: 'boardsList',
11+
firstPageParam: 0,
12+
queryFn: getBoardList,
13+
getNextPageParamFn: (boardList) =>
14+
boardList.last ? undefined : boardList.number + 1,
3215
});
3316

34-
// 무한 스크롤 화면 가장 아래 부분 탐지하는 observer
35-
function Observer({ children }: { children: React.ReactNode }) {
36-
const { ref, inView } = useInView({ threshold: 1 });
37-
38-
useEffect(() => {
39-
if (inView && hasNextPage) {
40-
fetchNextPage();
41-
}
42-
}, [inView]);
43-
44-
if (!hasNextPage || !data) return null;
45-
return (
46-
<div ref={ref}>{isLoading || isFetchingNextPage ? children : null}</div>
47-
);
48-
}
49-
5017
return {
5118
Observer,
5219
data,

hooks/queries/useGetMyBoardList.tsx

+8-33
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,17 @@
11
'use client';
22

3-
import { useEffect } from 'react';
4-
import { useInView } from 'react-intersection-observer';
5-
import { useInfiniteQuery } from '@tanstack/react-query';
63
import getMyBoardList from '@/service/myPage';
74
import { MyBoardList } from '@/types/types';
8-
9-
const QUERY_KEY = ['myBoards'];
5+
import useInfiniteScroll from '../common/useInfiniteScroll';
106

117
export default function useGetMyBoardList() {
12-
const { data, fetchNextPage, isLoading, hasNextPage, isFetchingNextPage } =
13-
useInfiniteQuery({
14-
queryKey: QUERY_KEY,
15-
queryFn: ({ pageParam = 0 }): Promise<MyBoardList> =>
16-
getMyBoardList(pageParam),
17-
getNextPageParam: (lastPage, allPages) =>
18-
allPages.length < 20 ? allPages.length + 1 : undefined,
19-
select: (d) => ({
20-
pages: d.pages.flatMap((page) => page),
21-
pageParams: d.pageParams,
22-
}),
23-
});
24-
25-
// 무한 스크롤 화면 가장 아래 부분 탐지하는 observer
26-
function Observer({ children }: { children: React.ReactNode }) {
27-
const { ref, inView } = useInView({ threshold: 1 });
28-
29-
useEffect(() => {
30-
if (inView && hasNextPage) {
31-
fetchNextPage();
32-
}
33-
}, [inView]);
34-
35-
if (!hasNextPage || !data) return null;
36-
return (
37-
<div ref={ref}>{isLoading || isFetchingNextPage ? children : null}</div>
38-
);
39-
}
8+
const { data, isLoading, Observer } = useInfiniteScroll<MyBoardList>({
9+
queryKey: 'myBoards',
10+
firstPageParam: 0,
11+
queryFn: getMyBoardList,
12+
getNextPageParamFn: (boardlist) =>
13+
boardlist.last ? undefined : boardlist.number + 1,
14+
});
4015

4116
return {
4217
Observer,

hooks/queries/useGetMyBookmarkedBoardList.tsx

+3-11
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,24 @@ import { useInfiniteQuery } from '@tanstack/react-query';
66
import getMyBoardList from '@/service/myPage';
77
import { BookmarkedBoardList } from '@/types/types';
88

9-
const QUERY_KEY = ['bookmarkedBoards'];
10-
119
export default function useGetBookmarkedBoardList() {
1210
const { data, fetchNextPage, isLoading, hasNextPage, isFetchingNextPage } =
1311
useInfiniteQuery({
14-
queryKey: QUERY_KEY,
12+
queryKey: ['bookmarkedBoards'],
1513
queryFn: ({ pageParam = 0 }): Promise<BookmarkedBoardList> =>
1614
getMyBoardList(pageParam),
17-
getNextPageParam: (lastPage, allPages) =>
18-
allPages.length < 20 ? allPages.length + 1 : undefined,
19-
select: (d) => ({
20-
pages: d.pages.flatMap((page) => page),
21-
pageParams: d.pageParams,
22-
}),
15+
getNextPageParam: (boardlist) =>
16+
boardlist.last ? undefined : boardlist.number + 1,
2317
});
2418

2519
// 무한 스크롤 화면 가장 아래 부분 탐지하는 observer
2620
function Observer({ children }: { children: React.ReactNode }) {
2721
const { ref, inView } = useInView({ threshold: 1 });
28-
2922
useEffect(() => {
3023
if (inView && hasNextPage) {
3124
fetchNextPage();
3225
}
3326
}, [inView]);
34-
3527
if (!hasNextPage || !data) return null;
3628
return (
3729
<div ref={ref}>{isLoading || isFetchingNextPage ? children : null}</div>

service/board.ts

+8-23
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ interface PostCommentType {
1010
parentId: number;
1111
content: string;
1212
}
13-
interface GetListApiProps {
14-
pageParam: number;
15-
pageSize: number;
16-
}
1713

1814
export async function postBoard(postBoardData: PostBoardType) {
1915
const url = `endpoint/api/board/register`;
@@ -29,10 +25,16 @@ export async function postBoard(postBoardData: PostBoardType) {
2925
return response.json();
3026
}
3127

32-
export async function getBoardList({ pageParam, pageSize }: GetListApiProps) {
28+
export async function getBoardList(pageParam: number) {
29+
let url = `/endpoint/api/board/list?pageSize=5`;
30+
if (pageParam !== 0) {
31+
url += `&pageNumber=${pageParam}`;
32+
}
3333
try {
34-
const url = `/endpoint/api/board/list?pageNumber=${pageParam}&pageSize=${pageSize}`;
3534
const response = await fetch(url);
35+
if (response.status === 401) {
36+
throw new AuthError('로그인이 필요한 서비스입니다.');
37+
}
3638
if (!response.ok) {
3739
throw new Error(`서버오류:${response.status}`);
3840
}
@@ -59,20 +61,3 @@ export async function postComment(postCommentData: PostCommentType) {
5961
});
6062
return response.json();
6163
}
62-
63-
// export async function getCommentList({ pageParam, pageSize }: GetListApiProps) {
64-
// try {
65-
// const url = `/endpoint/api/reply/list?pageNumber=${pageParam}&pageSize=${pageSize}`;
66-
// const response = await fetch(url);
67-
// if (!response.ok) {
68-
// throw new Error(`서버오류:${response.status}`);
69-
// }
70-
// return await response.json();
71-
// } catch (error) {
72-
// if (error instanceof AuthError) {
73-
// window.location.replace('/auth/login');
74-
// alert(error.message);
75-
// }
76-
// throw error;
77-
// }
78-
// }

0 commit comments

Comments
 (0)