Skip to content

Commit 93eea3e

Browse files
committed
[feat] 커뮤니티 탭, 필터 패치로직
1 parent 055112a commit 93eea3e

File tree

5 files changed

+111
-78
lines changed

5 files changed

+111
-78
lines changed

src/domains/community/api/fetchPost.ts

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -33,63 +33,57 @@ export const fetchPostById = async (postId: ParamValue) => {
3333
}
3434
};
3535

36-
export const fetchPostByTab = async (category: string, lastId?: number): Promise<Post[] | null> => {
37-
try {
38-
const res = await fetch(
39-
`${getApi}/posts?categoryId=${tabItem.findIndex((tab) => tab.key === category)}&${lastId ? `lastId=${lastId}&` : ''}postSortStatus=LATEST`,
40-
{
41-
method: 'GET',
42-
cache: 'no-store',
43-
}
44-
);
45-
46-
const data = await res.json();
47-
if (!data) return null;
48-
49-
const filtered = data.data.filter((post: Post) => post.categoryName === category);
50-
return filtered;
51-
} catch (err) {
52-
console.error('글 목록 필터링 실패', err);
53-
return null;
54-
}
55-
};
56-
57-
export const fetchPostByFilter = async ({
58-
filter,
59-
lastId,
36+
export const fetchPostByTab = async ({
6037
category,
38+
filter = 'LATEST',
39+
lastId,
6140
lastLikeCount,
6241
lastCommentCount,
6342
}: {
64-
filter: string;
65-
lastId?: number;
6643
category?: string;
44+
filter?: 'LATEST' | 'POPULAR' | 'COMMENTS';
45+
lastId?: number;
6746
lastLikeCount?: number;
6847
lastCommentCount?: number;
69-
}) => {
48+
}): Promise<Post[] | null> => {
7049
try {
71-
let url;
50+
const params = new URLSearchParams();
51+
52+
if (category && category !== 'all') {
53+
const categoryId = tabItem.findIndex((tab) => tab.key === category);
54+
if (categoryId >= 0) {
55+
params.set('categoryId', categoryId.toString());
56+
}
57+
}
58+
59+
if (lastId) params.set('lastId', lastId.toString());
60+
7261
switch (filter) {
73-
case '인기순':
74-
url = `${getApi}/posts?${category ? `categoryId=${tabItem.findIndex((tab) => tab.key === category)}&` : ''}${lastId ? `lastId=${lastId}&` : ''}lastLikeCount=${lastLikeCount}&postSortStatus=POPULAR`;
62+
case 'POPULAR':
63+
if (lastLikeCount) params.set('lastLikeCount', lastLikeCount.toString());
64+
params.set('postSortStatus', 'POPULAR');
7565
break;
76-
case '댓글순':
77-
url = `${getApi}/posts?${category ? `categoryId=${tabItem.findIndex((tab) => tab.key === category)}&` : ''}${lastId ? `lastId=${lastId}&` : ''}lastCommentCount=${lastCommentCount}&postSortStatus=COMMENTS`;
66+
case 'COMMENTS':
67+
if (lastCommentCount) params.set('lastCommentCount', lastCommentCount.toString());
68+
params.set('postSortStatus', 'COMMENTS');
7869
break;
70+
case 'LATEST':
7971
default:
80-
url = `${getApi}/posts?${category ? `categoryId=${tabItem.findIndex((tab) => tab.key === category)}&` : ''}${lastId ? `lastId=${lastId}&` : ''}postSortStatus=LATEST`;
72+
params.set('postSortStatus', 'LATEST');
8173
break;
8274
}
8375

84-
const res = await fetch(url ?? '', {
76+
const res = await fetch(`${getApi}/posts?${params.toString()}`, {
8577
method: 'GET',
8678
cache: 'no-store',
8779
});
8880

8981
const data = await res.json();
9082
if (!data) return null;
83+
84+
return data.data; // 필요하다면 filter 추가 가능
9185
} catch (err) {
92-
console.error('글 목록 필터링 실패', err);
86+
console.error('글 목록 가져오기 실패', err);
9387
return null;
9488
}
9589
};

src/domains/community/main/Community.tsx

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,89 @@
11
'use client';
22

3-
import { useEffect, useState } from 'react';
3+
import { useEffect, useMemo, useState } from 'react';
44
import CommunityFilter from './CommunityFilter';
55
import CommunityTab from './CommunityTab';
66
import PostCard from './PostCard';
77
import WriteBtn from './WriteBtn';
88
import { Post } from '../types/post';
9-
import { getApi } from '@/app/api/config/appConfig';
10-
import { fetchPost } from '../api/fetchPost';
9+
import { fetchPostByTab } from '../api/fetchPost';
10+
import { useSearchParams } from 'next/navigation';
1111

1212
function Community() {
1313
const [posts, setPosts] = useState<Post[] | null>([]);
1414
const [isLoading, setIsLoading] = useState(true);
15+
const [lastLoadedId, setLastLoadedId] = useState<number | null>(null);
16+
17+
const searchParams = useSearchParams();
18+
19+
const category = useMemo(() => searchParams.get('category') || 'all', [searchParams]);
20+
const filter = useMemo(
21+
() => (searchParams.get('postSortStatus') as 'LATEST' | 'POPULAR' | 'COMMENTS') || 'LATEST',
22+
[searchParams]
23+
);
1524

1625
const [isEnd, setIsEnd] = useState(false);
1726

27+
useEffect(() => {
28+
setPosts([]);
29+
setIsEnd(false);
30+
setLastLoadedId(null);
31+
loadInitialPosts();
32+
}, [category, filter]);
33+
34+
const loadInitialPosts = async () => {
35+
const category = searchParams.get('category') || 'all';
36+
const filter =
37+
(searchParams.get('postSortStatus') as 'LATEST' | 'POPULAR' | 'COMMENTS') || 'LATEST';
38+
39+
setIsLoading(true);
40+
setIsEnd(false);
41+
42+
// const latestPost = posts?.sort((a, b) => Number(b.createdAt) - Number(a.createdAt))
43+
// const latestLike = latestPost?.sort((a,b) => b.likeCount - a.likeCount );
44+
// const latestComment = latestPost?.sort((a,b) => b.commentCount - a.commentCount );
45+
46+
try {
47+
const newPosts = await fetchPostByTab({
48+
category,
49+
filter,
50+
});
51+
52+
if (!newPosts || newPosts.length === 0) {
53+
setIsEnd(true);
54+
setPosts([]);
55+
} else {
56+
setPosts(newPosts);
57+
}
58+
} finally {
59+
setIsLoading(false);
60+
}
61+
};
62+
1863
const loadMorePosts = async (lastPostId: number) => {
1964
if (isEnd || isLoading) return;
20-
console.log('시작');
65+
if (!posts || posts.length === 0) return;
66+
console.log('시작', lastPostId);
67+
68+
const lastPost = posts[posts.length - 1];
69+
if (lastPostId === lastPost.postId) return;
70+
setLastLoadedId(lastPost.postId);
2171

2272
setIsLoading(true);
2373
try {
24-
const newPosts = await fetchPost(lastPostId);
25-
console.log(newPosts);
74+
const category = searchParams.get('category') || 'all';
75+
const filter =
76+
(searchParams.get('postSortStatus') as 'LATEST' | 'POPULAR' | 'COMMENTS') || 'LATEST';
77+
78+
const newPosts = await fetchPostByTab({
79+
category,
80+
filter,
81+
lastId: lastPostId,
82+
});
2683

27-
if (newPosts?.length === 0) {
84+
if (!newPosts || newPosts?.length === 0) {
2885
setIsEnd(true);
86+
console.log('끝');
2987
} else {
3088
setPosts((prev) => [...(prev ?? []), ...(newPosts ?? [])]);
3189
}
@@ -40,7 +98,7 @@ function Community() {
4098
aria-label="탭과 글쓰기"
4199
className="flex justify-between item-center sm:flex-row flex-col gap-4 mt-1"
42100
>
43-
<CommunityTab setPosts={setPosts} setIsLoading={setIsLoading} />
101+
<CommunityTab setPosts={setPosts} setIsLoading={setIsLoading} setIsEnd={setIsEnd} />
44102
<WriteBtn />
45103
</section>
46104

src/domains/community/main/CommunityFilter.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { Post } from '../types/post';
44
import SelectBox from '@/shared/components/select-box/SelectBox';
55
import { Dispatch, SetStateAction, useEffect } from 'react';
6-
import { fetchPostByFilter } from '../api/fetchPost';
6+
import { fetchPostByTab } from '../api/fetchPost';
77
import { useRouter, useSearchParams } from 'next/navigation';
88

99
type Props = {
@@ -22,17 +22,20 @@ function CommunityFilter({ posts, setPosts }: Props) {
2222
const query = searchParams.get('category');
2323
const router = useRouter();
2424

25+
const currentCategory = searchParams.get(query as string) || 'all';
26+
2527
useEffect(() => {
2628
console.log(query);
2729
}, [query]);
2830

2931
const handleChange = async (selectTitle: string) => {
3032
if (!query) return;
33+
3134
console.log(selectTitle);
3235

33-
const data = await fetchPostByFilter({
34-
filter: selectTitle,
36+
const data = await fetchPostByTab({
3537
category: query,
38+
filter: sortMap[selectTitle as keyof typeof sortMap],
3639
});
3740
if (!data) return;
3841
setPosts(data);

src/domains/community/main/CommunityTab.tsx

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { useRouter, useSearchParams } from 'next/navigation';
99
type Props = {
1010
setPosts: (value: Post[] | null) => void;
1111
setIsLoading: (value: boolean) => void;
12+
setIsEnd: (value: boolean) => void;
1213
};
1314

1415
export const tabItem = [
@@ -19,31 +20,18 @@ export const tabItem = [
1920
{ key: 'chat', label: '자유' },
2021
];
2122

22-
function CommunityTab({ setPosts, setIsLoading }: Props) {
23+
function CommunityTab({ setPosts, setIsLoading, setIsEnd }: Props) {
2324
const searchParams = useSearchParams();
2425
const router = useRouter();
2526

2627
const currentSort = searchParams.get('postSortStatus') || 'LATEST';
27-
console.log(currentSort);
2828

2929
const [selectedCategory, setSelectedCategory] = useState(() => {
3030
const param = searchParams.get('category') || 'all';
31-
const isValid = tabItem.map(({ key }) => key === param);
32-
return isValid ? param : 'all';
31+
const exists = tabItem.some(({ key }) => key === param);
32+
return exists ? param : 'all';
3333
});
3434

35-
const handleTab = async (category: string) => {
36-
setIsLoading(true);
37-
if (category === 'all') {
38-
const data = await fetchPost();
39-
setPosts(data);
40-
} else {
41-
const data = await fetchPostByTab(category);
42-
setPosts(data);
43-
}
44-
setIsLoading(false);
45-
};
46-
4735
return (
4836
<section className="relative sm:w-[70%] w-full" aria-label="커뮤니티 탭">
4937
<div className="w-full overflow-x-scroll no-scrollbar scroll-smooth">
@@ -56,8 +44,10 @@ function CommunityTab({ setPosts, setIsLoading }: Props) {
5644
tabIndex={selectedCategory === key ? 0 : -1}
5745
onClick={() => {
5846
setSelectedCategory(key);
59-
router.push(`?category=${key}`);
60-
handleTab(key);
47+
const params = new URLSearchParams();
48+
params.set('category', key);
49+
params.set('postSortStatus', currentSort); // ✅ 현재 필터 상태 유지
50+
router.push(`?${params.toString()}`);
6151
}}
6252
className={tw(
6353
`border-1 py-1 px-3 rounded-2xl transition-colors ease-in min-w-18`,

src/domains/community/main/PostCard.tsx

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ import { Post } from '@/domains/community/types/post';
99
import { useRouter } from 'next/navigation';
1010
import SkeletonPostCard from '@/domains/shared/skeleton/SkeletonPostCard';
1111
import { useInfiniteScrollObserver } from '@/shared/hook/useInfiniteScrollObserver';
12-
import { useEffect, useRef } from 'react';
13-
import { fetchPost } from '../api/fetchPost';
12+
import { useRef } from 'react';
1413

1514
type Props = {
1615
posts: Post[] | null;
@@ -21,25 +20,14 @@ type Props = {
2120
onLoadMore?: (lastCommentId: number) => void;
2221
};
2322

24-
function PostCard({ posts, setPost, isLoading, setIsLoading, isEnd, onLoadMore }: Props) {
23+
function PostCard({ posts, isLoading, isEnd, onLoadMore }: Props) {
2524
const router = useRouter();
2625
const firstItemRef = useRef<HTMLElement | null>(null);
2726

2827
const handlePost = (id: number) => {
2928
router.push(`/community/${id}`);
3029
};
3130

32-
useEffect(() => {
33-
const fetchData = async () => {
34-
setIsLoading(true);
35-
const data = await fetchPost();
36-
if (!data) return;
37-
setPost(data);
38-
setIsLoading(false);
39-
};
40-
fetchData();
41-
}, [setPost, setIsLoading]);
42-
4331
const observeLastItem = useInfiniteScrollObserver<HTMLElement>({
4432
items: posts,
4533
isEnd,

0 commit comments

Comments
 (0)