Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/#37: 게시글 상세 댓글, 좋아요, 스크랩 기능 구현 #53

Merged
merged 66 commits into from
Mar 9, 2024

Conversation

semnil5202
Copy link
Member

@semnil5202 semnil5202 commented Mar 6, 2024

📄 Summary

close #37

게시글 상세 댓글, 대댓글, 좋아요, 스크랩 기능을 구현합니다.

디자인 시스템 반영 사항은 다음과 같습니다. 0.4.2 PR, 0.4.3 PR 두 PR 모두 기존 컴포넌트 UI 오류나 기능을 추가한 것으로 기존의 사용 방법이 변경되어 오류를 발생시키거나 하지는 않습니다.

🙋🏻 More

서버와 실 연동 작업이 필요합니다. 몇몇 기능이 동작하지 않습니다. 자세한 내용은 이슈에서 확인 가능합니다.

추가로 중복 코드가 좀 있습니다. 그리고 댓글 창 확대 시 포커싱 하는 기능이 WriteComment, WriteRecomment 내부에 있으며 상태와 ref는 CommentFocusContext에서 가져옵니다. 이 부분이 살짝 복잡할 수 있습니다.

서버와 연동 작업 완료 후 ready for review 상태로 변경해두겠습니다.

댓글 및 대댓글 동작 (포커싱, 확대 등)

PC 환경

Mar-07-2024 05-04-32

Mobile 가상키보드 환경

Mar-07-2024 05-06-39

@semnil5202 semnil5202 added the feat 기능 구현 label Mar 6, 2024
@semnil5202 semnil5202 requested a review from yogjin March 6, 2024 20:27
@semnil5202 semnil5202 self-assigned this Mar 6, 2024
@semnil5202 semnil5202 marked this pull request as ready for review March 7, 2024 14:48
Copy link
Collaborator

@yogjin yogjin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다. 로직이 많아서 눈에 띄는 것 위주로 짧게 리뷰했습니다!

Comment on lines 19 to 36
const usePostCommentMutation = ({ feedId, onSuccess }: Props) => {
const queryClient = useQueryClient();
const { mutate: postComment, ...rest } = useMutation({
mutationFn: _postComment,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['comments', feedId] });
queryClient.invalidateQueries({ queryKey: ['feedDetail', feedId] });
if (onSuccess) onSuccess();
},
onError: (error: AxiosError<{ message: string }>) => {
alert(error.response?.data.message ?? '댓글 작성에 실패했습니다.');
},
});

return { postComment, ...rest };
};

export default usePostCommentMutation;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onSuccess를 prop으로 또 선언을 해줄필요가 없습니다.
...rest로 넘겨주었기 때문에, mutation 사용처에서 option으로 onSuccess 콜백을 선언해주면, 알아서 처리가 되더라구요.
사실 저도 처음 react-query쓸 때 같은 실수를 했습니다😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하 사용처에서 option으로 onSuccess 콜백을 선언해준다는게 어떻게 사용되는 것인지 알 수 있을까여? 처음 안 사실이군요


const _editComment = (commentId: string, payload: CommentPayload) => http.patch(`/comments/${commentId}`, payload);

const usePatchCommentMutation = ({ feedId, commentId, onSuccess }: Props) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 mutation은 따로 뒤에 붙이지 않는 편입니다.
Patch, Post, Put 처럼 앞에서 붙여져 있기 때문인데요, 이건 취향차이라고 생각합니다.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호 저도 Mutation postfix를 계속 붙이다보니 네이밍이 너무 길어지더군요. Delete, Patch, Put 키워드를 삭제하자니 기능이 제대로 명시가 안되는 것 같고..

Mutation postfix 일괄 삭제할께여~


return (
<>
<CommentFocusProvider>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이게 어떤역할을 하나요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요 부분이 아래 세 가지 기능을 컨트롤 합니다.

  1. 댓글 입력창 상태 관리 및 포커싱, 포커싱 아웃 로직 관리 (댓글 맨 위 상단의 댓글 입력창)
  2. 대댓글 입력창 포커싱, 포커싱 아웃 로직 관리 (각 댓글 마다 생성 가능한 대댓글 입력창)
  3. 댓글 및 대댓글 수정 입력창 포커싱, 포커싱 아웃 로직 관리 (각 댓글, 대댓글 수정 시 보이는 입력창)

2번과 3번은 동일하게 생겼지만 1번이 조금 특이합니다. 1번의 경우에는 게시글 상세 통 틀어서 하나의 입력창이고 입력 값에 따라서 댓글창을 확장할지 축소할지 등의 로직이 추가됩니다. 그래서 컨텍스트 내부에 1번 입력창의 상태가 있고 2번, 3번 입력창은 각 컴포넌트의 로컬 상태로 관리됩니다. (각 댓글, 대댓글 마다 생성해야하므로)

위 기능을 관리하는 컨텍스트가 되겠습니다.

<Box padding="20px 22px">
<WriteComment feedId={feedId} myImageUrl={myImageUrl} myNickname={myNickname} />
{comments.map((comment, idx) => (
<Fragment key={comment.content}>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key를 comment.parentCommentId 로 하는건 어떤가요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분 역시 수정해두겠습니다.

Comment on lines 3 to 14
type CommentFocusContextType = {
isFocusComment: boolean;
openCommentTextarea: () => void;
focusRecommentTextarea: () => void;
closeCommentTextarea: () => void;
initRecommentTextareaRef: () => void;
focusEditCommentTextarea: () => void;
initEditCommentTextarea: () => void;
commentTextareaRef: MutableRefObject<HTMLTextAreaElement | null>;
recommentTextareaRef: MutableRefObject<HTMLTextAreaElement | null>;
editCommentTextareaRef: MutableRefObject<HTMLTextAreaElement | null>;
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

와우.... 스크롤하는 부분 로직 같은데 복잡하네요ㅋㅋㅋㅋ

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 이 부분이 좀 많이 어지러운데 그렇다고 컨텍스트를 여러개로 나눠서 컨텍스트 세 개를 감싸자니 좀 애매하고..

그래서 로직은 한 곳으로 몰아두고 커스텀 훅을 활용해서 가져오는 값을 제한하는 방식으로 진행했습니다.

const { mutate: deleteScrap, ...rest } = useMutation({
mutationFn: _deleteScrap,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['feedDetail', id] });
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

queryKey 선언 방식이 약간 어색하게 느껴집니다.
['feeds', 'detail', id] 는 어떤가요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오우 좋습니다. 반영해둘께요.

Comment on lines 17 to 36
const _postComment = (payload: CommentPayload) => http.post('/comments', payload);

const usePostCommentMutation = ({ feedId, onSuccess }: Props) => {
const queryClient = useQueryClient();
const { mutate: postComment, ...rest } = useMutation({
mutationFn: _postComment,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['comments', feedId] });
queryClient.invalidateQueries({ queryKey: ['feedDetail', feedId] });
if (onSuccess) onSuccess();
},
onError: (error: AxiosError<{ message: string }>) => {
alert(error.response?.data.message ?? '댓글 작성에 실패했습니다.');
},
});

return { postComment, ...rest };
};

export default usePostCommentMutation;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

queryKey ['comments', feedId] 부분이 잘 이해가 되지 않습니다. 해석 가능할까요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

댓글 무한스크롤 조회 쿼리키 입니다. 각 게시글 id 값으로 댓글을 조회하고 있는데, 댓글을 작성하거나 삭제한 후 해당 쿼리 키를 무효화하여 댓글 리스트를 최신화 하고 있습니다.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇군요. 더 나은 쿼리키 선언 방법이 있는지 고민해보면 좋을 것 같아요.
저도 당장은 모르겠는데, comments 뒤에 feed의 id가 나오니까 조금 어색한 느낌이 들었네요

Comment on lines 118 to 120

{commentChildResponses.map((recomment, idx) => (
<Recomment key={idx} recomment={recomment} />
<Recomment key={idx} feedId={feedId} recomment={recomment} myImageUrl={myImageUrl} myNickname={myNickname} />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분도 key로 recomment.childCommentId는 어떨까요

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오우 좋습니다. id 값이 서버에 반영되기 전에 로직을 미쳐 수정하지못했군요. 수정해두겠습니다.

@semnil5202
Copy link
Member Author

추가로 현재 서버에서 댓글 및 대댓글 좋아요, 좋아요 해제 기능이 도입되어서 이 부분과 리팩토링을 같이 반영해두도록 하겠습니다.

Copy link
Collaborator

@yogjin yogjin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨어요

@semnil5202 semnil5202 merged commit 3c02e71 into develop Mar 9, 2024
@yogjin yogjin deleted the feat/#37 branch March 9, 2024 11:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feat 기능 구현
Projects
None yet
Development

Successfully merging this pull request may close these issues.

게시글 상세 댓글, 좋아요, 스크랩 기능 구현
2 participants