Skip to content

Commit f399712

Browse files
authored
Merge pull request #121 from oodd-team/feat/OD-168
[OD-168] Post 페이지 리팩토링
2 parents b04aa20 + 5a1db3e commit f399712

5 files changed

Lines changed: 186 additions & 182 deletions

File tree

src/pages/Post/PostBase/LikeCommentBottomSheetContent/CommentItem/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414

1515
import { StyledText } from '@components/Text/StyledText';
1616

17-
import { CommentItemProps } from './dto';
17+
import type { CommentItemProps } from './dto';
1818

1919
import More from '@assets/default/more.svg';
2020

src/pages/Post/PostBase/LikeCommentBottomSheetContent/MenuButtonList/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import ReactDOM from 'react-dom';
3-
import { MenuButtonListProps } from './dto';
3+
import type { MenuButtonListProps } from './dto';
44
import { MenuListWrapper, MenuListContainer, MenuButtonItem } from './styles';
55
import { StyledText } from '@components/Text/StyledText';
66

src/pages/Post/PostBase/LikeCommentBottomSheetContent/index.tsx

Lines changed: 71 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ import { StyledText } from '@components/Text/StyledText';
1414
import theme from '@styles/theme';
1515
import Loading from '@components/Loading';
1616
import Modal from '@components/Modal';
17-
import CommentItem from './CommentItem';
18-
import MenuButtonList from './MenuButtonList';
17+
import CommentItem from './CommentItem/index';
18+
import MenuButtonList from './MenuButtonList/index';
1919

20-
import { LikeCommentBottomSheetProps } from '../dto';
21-
import { ModalProps } from '@components/Modal/dto';
22-
import { GetPostLikeListResponse } from '@apis/post-like/dto';
23-
import { Comment, GetCommentListResponse } from '@apis/post-comment/dto';
20+
import type { LikeCommentBottomSheetProps } from '../dto';
21+
import type { ModalProps } from '@components/Modal/dto';
22+
import type { GetPostLikeListResponse } from '@apis/post-like/dto';
23+
import type { Comment, GetCommentListResponse } from '@apis/post-comment/dto';
2424

2525
import Delete from '@assets/default/delete.svg';
2626
import Block from '@assets/default/block.svg';
@@ -36,15 +36,11 @@ import { getCurrentUserId } from '@utils/getCurrentUserId';
3636

3737
const LikeCommentBottomSheetContent: React.FC<LikeCommentBottomSheetProps> = ({ tab, likeCount, commentCount }) => {
3838
const [activeTab, setActiveTab] = useState<'likes' | 'comments'>(tab);
39-
const { postId } = useParams<{ postId: string }>();
4039

4140
const [likes, setLikes] = useState<GetPostLikeListResponse['data']['likes']>([]);
4241
const [postLikeCount, setPostLikeCount] = useState(likeCount);
4342
const [comments, setComments] = useState<GetCommentListResponse['data']['comments']>([]);
4443
const [postCommentCount, setPostCommentCount] = useState(commentCount);
45-
const [isBlockConfirmationModalOpen, setIsBlockConfirmationModalOpen] = useState(false);
46-
const [isStatusModalOpen, setIsStatusModalOpen] = useState(false);
47-
const [modalContent, setModalContent] = useState('알 수 없는 오류입니다.\n관리자에게 문의해 주세요.');
4844

4945
const [isLoading, setIsLoading] = useState(false);
5046
const [page, setPage] = useState(1);
@@ -60,50 +56,43 @@ const LikeCommentBottomSheetContent: React.FC<LikeCommentBottomSheetProps> = ({
6056
const [isMenuVisible, setIsMenuVisible] = useState(false);
6157
const [menuPosition, setMenuPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 });
6258

59+
const [isBlockConfirmationModalOpen, setIsBlockConfirmationModalOpen] = useState(false);
60+
const [isStatusModalOpen, setIsStatusModalOpen] = useState(false);
6361
const [isCommentDeleteConfirmationModalOpen, setIsCommentDeleteConfirmationModalOpen] = useRecoilState(
6462
IsCommentDeleteConfirmationModalOpenAtom,
6563
);
6664
const [, setIsCommentReportModalOpen] = useRecoilState(IsCommentReportModalOpenAtom);
65+
const [modalContent, setModalContent] = useState('알 수 없는 오류입니다.\n관리자에게 문의해 주세요.');
6766

67+
const { postId } = useParams<{ postId: string }>();
6868
const nav = useNavigate();
6969

70-
useEffect(() => {
71-
setPage(1);
72-
setReachedEnd(false);
73-
setLikes([]);
74-
setComments([]);
75-
76-
if (activeTab === 'likes') {
77-
getPostLikeList(1);
78-
} else if (activeTab === 'comments') {
79-
getPostCommentList();
80-
}
81-
}, [activeTab]);
82-
83-
// IntersectionObserver를 활용하여 무한 스크롤 감지
84-
useEffect(() => {
85-
if (observerRef.current) observerRef.current.disconnect();
86-
87-
const handleIntersection = (entries: IntersectionObserverEntry[]) => {
88-
const [entry] = entries;
89-
if (entry.isIntersecting && !isLoading) {
90-
console.log('호출');
91-
getPostLikeList(page);
92-
}
93-
};
70+
// 댓글 메뉴 클릭한 경우
71+
const handleMenuOpen = (comment: Comment, event: React.MouseEvent<HTMLButtonElement>) => {
72+
setSelectedComment(comment);
73+
const rect = event.currentTarget.getBoundingClientRect();
74+
setMenuPosition({ top: rect.bottom + window.scrollY - 90, left: rect.left + window.scrollX - 100 });
75+
setIsMenuVisible(true);
76+
};
9477

95-
observerRef.current = new IntersectionObserver(handleIntersection, {
96-
root: null,
97-
rootMargin: '0px',
98-
threshold: 1.0,
99-
});
78+
// 유저 클릭한 경우
79+
const handleUserClick = (userId: number) => {
80+
// 로컬 스토리지에서 사용자 ID 가져오기
81+
const myUserId = getCurrentUserId(); // 로컬 스토리지에 저장된 사용자 ID를 가져옴
10082

101-
if (loadMoreRef.current) observerRef.current.observe(loadMoreRef.current);
83+
if (String(myUserId) === String(userId)) {
84+
// 나인 경우
85+
nav(`/profile/${userId}`);
86+
} else {
87+
// 다른 유저인 경우
88+
nav(`/users/${userId}`);
89+
}
90+
};
10291

103-
return () => {
104-
if (observerRef.current) observerRef.current.disconnect();
105-
};
106-
}, [page, reachedEnd, loadMoreRef.current, activeTab]);
92+
// 댓글 작성 Input
93+
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
94+
setInputValue(e.target.value);
95+
}, []);
10796

10897
// 좋아요 리스트 불러오기 api
10998
const getPostLikeList = async (currentPage: number) => {
@@ -156,11 +145,6 @@ const LikeCommentBottomSheetContent: React.FC<LikeCommentBottomSheetProps> = ({
156145
}
157146
};
158147

159-
// 댓글 작성 Input
160-
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
161-
setInputValue(e.target.value);
162-
}, []);
163-
164148
// 댓글 작성 api
165149
const createComment = async () => {
166150
if (isSubmitting) return; // 중복 요청 방지
@@ -233,6 +217,44 @@ const LikeCommentBottomSheetContent: React.FC<LikeCommentBottomSheetProps> = ({
233217
}
234218
};
235219

220+
useEffect(() => {
221+
setPage(1);
222+
setReachedEnd(false);
223+
setLikes([]);
224+
setComments([]);
225+
226+
if (activeTab === 'likes') {
227+
getPostLikeList(1);
228+
} else if (activeTab === 'comments') {
229+
getPostCommentList();
230+
}
231+
}, [activeTab]);
232+
233+
// IntersectionObserver를 활용하여 무한 스크롤 감지
234+
useEffect(() => {
235+
if (observerRef.current) observerRef.current.disconnect();
236+
237+
const handleIntersection = (entries: IntersectionObserverEntry[]) => {
238+
const [entry] = entries;
239+
if (entry.isIntersecting && !isLoading) {
240+
console.log('호출');
241+
getPostLikeList(page);
242+
}
243+
};
244+
245+
observerRef.current = new IntersectionObserver(handleIntersection, {
246+
root: null,
247+
rootMargin: '0px',
248+
threshold: 1.0,
249+
});
250+
251+
if (loadMoreRef.current) observerRef.current.observe(loadMoreRef.current);
252+
253+
return () => {
254+
if (observerRef.current) observerRef.current.disconnect();
255+
};
256+
}, [page, reachedEnd, loadMoreRef.current, activeTab]);
257+
236258
// 본인 댓글 메뉴 항목
237259
const MyCommentMenuItems = [
238260
{
@@ -307,28 +329,6 @@ const LikeCommentBottomSheetContent: React.FC<LikeCommentBottomSheetProps> = ({
307329
},
308330
};
309331

310-
// 댓글 메뉴 클릭한 경우
311-
const handleMenuOpen = (comment: Comment, event: React.MouseEvent<HTMLButtonElement>) => {
312-
setSelectedComment(comment);
313-
const rect = event.currentTarget.getBoundingClientRect();
314-
setMenuPosition({ top: rect.bottom + window.scrollY - 90, left: rect.left + window.scrollX - 100 });
315-
setIsMenuVisible(true);
316-
};
317-
318-
// 유저 클릭한 경우
319-
const handleUserClick = (userId: number) => {
320-
// 로컬 스토리지에서 사용자 ID 가져오기
321-
const myUserId = getCurrentUserId(); // 로컬 스토리지에 저장된 사용자 ID를 가져옴
322-
323-
if (String(myUserId) === String(userId)) {
324-
// 나인 경우
325-
nav(`/profile/${userId}`);
326-
} else {
327-
// 다른 유저인 경우
328-
nav(`/users/${userId}`);
329-
}
330-
};
331-
332332
return (
333333
<>
334334
<TabContainer>

src/pages/Post/PostBase/index.tsx

Lines changed: 58 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import TopBar from '@components/TopBar';
1515
import NavBar from '@components/NavBar';
1616
import BottomSheet from '@components/BottomSheet';
1717
import ClothingInfoItem from '@components/ClothingInfoItem';
18-
import ImageSwiper from './ImageSwiper';
19-
import LikeCommentBottomSheetContent from './LikeCommentBottomSheetContent';
18+
import ImageSwiper from './ImageSwiper/index';
19+
import LikeCommentBottomSheetContent from './LikeCommentBottomSheetContent/index';
2020

2121
import {
2222
PostLayout,
@@ -36,22 +36,21 @@ import {
3636
ClothingInfoList,
3737
} from './styles';
3838

39-
import Left from '../../../assets/arrow/left.svg';
40-
import Like from '../../../assets/default/like.svg';
41-
import LikeFill from '../../../assets/default/like-fill.svg';
42-
import Message from '../../../assets/default/message.svg';
43-
import More from '../../../assets/default/more.svg';
39+
import Left from '@assets/arrow/left.svg';
40+
import Like from '@assets/default/like.svg';
41+
import LikeFill from '@assets/default/like-fill.svg';
42+
import Message from '@assets/default/message.svg';
43+
import More from '@assets/default/more.svg';
4444

45-
import { BottomSheetProps } from '@components/BottomSheet/dto';
46-
import { PostBaseProps } from './dto';
47-
import { GetPostDetailResponse } from '@apis/post/dto';
45+
import type { BottomSheetProps } from '@components/BottomSheet/dto';
46+
import type { PostBaseProps } from './dto';
47+
import type { GetPostDetailResponse } from '@apis/post/dto';
4848

4949
import { getPostDetailApi } from '@apis/post';
5050
import { togglePostLikeStatusApi } from '@apis/post-like';
5151
import { getCurrentUserId } from '@utils/getCurrentUserId';
5252

5353
const PostBase: React.FC<PostBaseProps> = ({ onClickMenu }) => {
54-
const { postId } = useParams<{ postId: string }>();
5554
const [, setPostId] = useRecoilState(postIdAtom);
5655
const [post, setPost] = useState<GetPostDetailResponse['data']>();
5756
const [user, setUser] = useRecoilState(userAtom);
@@ -62,9 +61,56 @@ const PostBase: React.FC<PostBaseProps> = ({ onClickMenu }) => {
6261
const [isLikeCommentBottomSheetOpen, setIsLikeCommentBottomSheetOpen] = useState(false);
6362
const [activeTab, setActiveTab] = useState<'likes' | 'comments'>('likes'); // activeTab state
6463

65-
const nav = useNavigate();
64+
const { postId } = useParams<{ postId: string }>();
6665
const userId = getCurrentUserId();
6766

67+
const nav = useNavigate();
68+
69+
const handleLikeCommentOpen = (tab: 'likes' | 'comments') => {
70+
setActiveTab(tab); // 클릭한 버튼에 따라 activeTab 설정
71+
setIsLikeCommentBottomSheetOpen(true);
72+
};
73+
74+
const handleUserClick = () => {
75+
if (post?.isPostWriter) {
76+
// 내 게시물인 경우
77+
nav(`/profile/${userId}`);
78+
} else {
79+
// 다른 유저의 게시물인 경우
80+
nav(`/users/${post?.user.id}`);
81+
}
82+
};
83+
84+
const contentRef = useRef<HTMLDivElement>(null);
85+
86+
const toggleTextDisplay = () => {
87+
setShowFullText((prev) => !prev);
88+
};
89+
90+
// 게시글 좋아요 누르기/취소하기 api
91+
const togglePostLikeStatus = async () => {
92+
if (!post || !postId) return;
93+
94+
const prevPost = { ...post }; // 현재 상태 저장
95+
setPost({
96+
...post,
97+
isPostLike: !post.isPostLike,
98+
postLikesCount: post.isPostLike ? post.postLikesCount - 1 : post.postLikesCount + 1,
99+
}); //사용자가 좋아요를 누르면 먼저 클라이언트에서 post 상태를 변경(낙관적 업데이트)
100+
101+
try {
102+
const response = await togglePostLikeStatusApi(Number(postId));
103+
setPost({
104+
...post,
105+
isPostLike: response.data.isPostLike,
106+
postLikesCount: response.data.postLikesCount,
107+
}); // 서버로 요청 후 성공하면 그대로 유지
108+
} catch (error) {
109+
console.error('Error toggling like status:', error);
110+
setPost(prevPost); // 실패하면 원래 상태로 롤백
111+
}
112+
};
113+
68114
useEffect(() => {
69115
setPostId(Number(postId));
70116

@@ -85,8 +131,6 @@ const PostBase: React.FC<PostBaseProps> = ({ onClickMenu }) => {
85131
getPost();
86132
}, [postId]);
87133

88-
const contentRef = useRef<HTMLDivElement>(null);
89-
90134
useEffect(() => {
91135
if (contentRef.current) {
92136
// 실제 높이와 줄 제한 높이 비교
@@ -95,25 +139,6 @@ const PostBase: React.FC<PostBaseProps> = ({ onClickMenu }) => {
95139
}
96140
}, [post?.content]);
97141

98-
const toggleTextDisplay = () => {
99-
setShowFullText((prev) => !prev);
100-
};
101-
102-
const handleUserClick = () => {
103-
if (post?.isPostWriter) {
104-
// 내 게시물인 경우
105-
nav(`/profile/${userId}`);
106-
} else {
107-
// 다른 유저의 게시물인 경우
108-
nav(`/users/${post?.user.id}`);
109-
}
110-
};
111-
112-
const handleLikeCommentOpen = (tab: 'likes' | 'comments') => {
113-
setActiveTab(tab); // 클릭한 버튼에 따라 activeTab 설정
114-
setIsLikeCommentBottomSheetOpen(true);
115-
};
116-
117142
const likeCommentbottomSheetProps: BottomSheetProps = {
118143
isOpenBottomSheet: isLikeCommentBottomSheetOpen,
119144
isHandlerVisible: true,
@@ -128,30 +153,6 @@ const PostBase: React.FC<PostBaseProps> = ({ onClickMenu }) => {
128153
},
129154
};
130155

131-
// 게시글 좋아요 누르기/취소하기
132-
const togglePostLikeStatus = async () => {
133-
if (!post || !postId) return;
134-
135-
const prevPost = { ...post }; // 현재 상태 저장
136-
setPost({
137-
...post,
138-
isPostLike: !post.isPostLike,
139-
postLikesCount: post.isPostLike ? post.postLikesCount - 1 : post.postLikesCount + 1,
140-
}); //사용자가 좋아요를 누르면 먼저 클라이언트에서 post 상태를 변경(낙관적 업데이트)
141-
142-
try {
143-
const response = await togglePostLikeStatusApi(Number(postId));
144-
setPost({
145-
...post,
146-
isPostLike: response.data.isPostLike,
147-
postLikesCount: response.data.postLikesCount,
148-
}); // 서버로 요청 후 성공하면 그대로 유지
149-
} catch (error) {
150-
console.error('Error toggling like status:', error);
151-
setPost(prevPost); // 실패하면 원래 상태로 롤백
152-
}
153-
};
154-
155156
return (
156157
<OODDFrame>
157158
<TopBar LeftButtonSrc={Left} />

0 commit comments

Comments
 (0)