feat: comments api 구현#80
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Walkthrough댓글 및 사용자 검색 기능이 서버 API와 연동되도록 주요 모듈이 추가 및 수정되었습니다. 댓글 조회, 삭제, 좋아요 API가 구현되어 UI 컴포넌트와 연결되었고, 답글 관련 모달 팝업 시스템이 확장되었습니다. 사용자 검색은 실시간 API 데이터 기반으로 동작하며, 관련 타입 및 목데이터 구조가 일관성 있게 리팩토링되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as Reply/SubReply 컴포넌트
participant Popup as PopupContainer/ReplyModal
participant API as 댓글 API 모듈
UI->>API: getComments(postId)
API-->>UI: 댓글/답글 리스트 반환
UI->>Popup: openReplyModal({replyId, ...})
Popup->>UI: 삭제/신고 선택 UI 표시
Popup->>API: deleteComment(replyId)
API-->>Popup: 삭제 결과 반환
Popup->>UI: 성공 시 댓글/답글 목록 갱신
UI->>API: postLike(replyId, type)
API-->>UI: 좋아요 상태/카운트 반환
UI->>UI: 상태 반영
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20분 Possibly related issues
Possibly related PRs
Suggested labels
Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (6)
src/components/common/Modal/ReplyModal.tsx (1)
62-64: 주석 처리된 신고 기능신고 기능이 주석 처리되어 있습니다. 향후 구현 예정인지 확인이 필요합니다.
신고 기능 구현을 도와드릴까요? 새로운 이슈로 등록하시겠습니까?
src/api/comments/deleteComment.ts (1)
3-5: 불필요한 인터페이스 정의
DeleteCommentRequest인터페이스가 정의되어 있지만 실제로는 사용되지 않습니다. 경로 매개변수를 사용하므로 별도 요청 인터페이스가 필요하지 않습니다.다음과 같이 인터페이스를 제거하세요:
-export interface DeleteCommentRequest { - commentId: number; -} -src/api/comments/getComments.ts (1)
57-58: URL 구성 로직을 단순화하세요.URLSearchParams는 빈 파라미터를 자동으로 처리하므로 조건부 URL 구성이 불필요합니다.
다음과 같이 단순화할 수 있습니다:
- const queryString = searchParams.toString(); - const url = queryString ? `/comments/${postId}?${queryString}` : `/comments/${postId}`; + const queryString = searchParams.toString(); + const url = `/comments/${postId}${queryString ? `?${queryString}` : ''}`;src/pages/feed/UserSearchResult.tsx (2)
22-25: 불필요하게 복잡한 빈 배열 체크 로직을 단순화하세요.배열이 비어있는지 확인하는 로직이 지나치게 장황합니다.
다음과 같이 간단하게 개선할 수 있습니다:
- const isEmptySearchedUserList = () => { - if (searchedUserList.length === 0) return true; - else return false; - }; + const isEmptySearchedUserList = () => searchedUserList.length === 0;또는 함수 없이 직접 사용:
- {isEmptySearchedUserList() ? ( + {searchedUserList.length === 0 ? (
30-45: Intersection Observer 정리 로직을 개선하세요.Observer 정리 시 observerRef.current 체크가 누락되어 있습니다.
다음과 같이 개선하세요:
- return () => observer.disconnect(); + return () => { + if (observerRef.current) { + observer.unobserve(observerRef.current); + } + observer.disconnect(); + };src/components/common/Post/SubReply.tsx (1)
73-73: 프로필 이미지 URL 처리를 개선하세요.빈 문자열 대신 undefined를 사용하는 것이 더 적절합니다.
- creatorProfileImageUrl={creatorProfileImageUrl || ''} + creatorProfileImageUrl={creatorProfileImageUrl || undefined}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
src/assets/header/header-logo.svgis excluded by!**/*.svg
📒 Files selected for processing (22)
index.html(1 hunks)src/api/comments/deleteComment.ts(1 hunks)src/api/comments/getComments.ts(1 hunks)src/api/comments/postLike.ts(1 hunks)src/api/users/getUsers.ts(1 hunks)src/components/common/Modal/PopupContainer.tsx(2 hunks)src/components/common/Modal/ReplyModal.tsx(1 hunks)src/components/common/Post/Reply.tsx(2 hunks)src/components/common/Post/ReplyList.tsx(1 hunks)src/components/common/Post/SubReply.tsx(3 hunks)src/components/feed/UserProfileItem.tsx(4 hunks)src/data/postData.ts(1 hunks)src/hooks/useDebounce.ts(1 hunks)src/hooks/usePopupActions.ts(2 hunks)src/hooks/useUserSearch.ts(1 hunks)src/pages/feed/FeedDetailPage.tsx(3 hunks)src/pages/feed/UserSearch.tsx(2 hunks)src/pages/feed/UserSearchResult.tsx(3 hunks)src/stores/usePopupStore.ts(2 hunks)src/types/post.ts(1 hunks)src/types/profile.ts(0 hunks)src/types/user.ts(1 hunks)
💤 Files with no reviewable changes (1)
- src/types/profile.ts
🧰 Additional context used
🧬 Code Graph Analysis (9)
src/pages/feed/UserSearch.tsx (2)
src/hooks/useUserSearch.ts (1)
useUserSearch(11-80)src/pages/feed/UserSearchResult.tsx (1)
UserSearchResult(15-70)
src/hooks/usePopupActions.ts (1)
src/stores/usePopupStore.ts (1)
ReplyModalProps(28-37)
src/components/common/Modal/PopupContainer.tsx (1)
src/stores/usePopupStore.ts (1)
ReplyModalProps(28-37)
src/pages/feed/UserSearchResult.tsx (1)
src/api/users/getUsers.ts (1)
UserData(3-10)
src/api/users/getUsers.ts (1)
src/api/index.ts (1)
apiClient(27-33)
src/api/comments/getComments.ts (1)
src/api/index.ts (1)
apiClient(27-33)
src/hooks/useUserSearch.ts (2)
src/api/users/getUsers.ts (2)
UserData(3-10)getUsers(26-42)src/hooks/useDebounce.ts (1)
useDebounce(3-17)
src/api/comments/deleteComment.ts (1)
src/api/index.ts (1)
apiClient(27-33)
src/api/comments/postLike.ts (1)
src/api/index.ts (1)
apiClient(27-33)
🔇 Additional comments (33)
index.html (1)
5-5: 절대 경로로 지정된 favicon 경로 호환성 검증 필요
- 위치:
index.html5행- 현재 코드:
<link rel="icon" type="image/svg+xml" href="/assets/custom_favicon.svg" />- 확인 결과:
public/assets/custom_favicon.svg파일이 리포지토리에 존재합니다.그러나
/assets/...와 같은 루트 절대 경로를 사용할 경우
– 애플리케이션을/app/등의 서브 디렉터리에 배포하거나
– Vite/Vercel 등에서base옵션을 설정했을 때favicon이 로드되지 않을 수 있습니다.
해결 방안 중 하나를 선택해 적용하거나, 배포 환경에서 실제 로드 여부를 확인해주세요:
- 상대 경로 사용
<link rel="icon" href="./assets/custom_favicon.svg" />- Vite의
import.meta.env.BASE_URL활용<link rel="icon" href="${import.meta.env.BASE_URL}assets/custom_favicon.svg" />- Vite 설정(
vite.config.js)의base옵션을 적절히 지정하고, 빌드 후 경로 매핑을 검증src/types/user.ts (1)
5-5: 명명 규칙 통일이 잘 적용되었습니다.
nickName에서nickname으로의 변경은 일관된 명명 규칙을 위한 좋은 개선입니다. 다른 파일들에서도 동일하게 적용된 것을 확인했습니다.src/hooks/useDebounce.ts (1)
3-17: 디바운스 훅 구현이 우수합니다.표준적인 디바운스 패턴을 잘 구현했습니다. 제네릭 타입 사용으로 재사용성이 높고, useEffect의 의존성 배열과 클린업 함수가 올바르게 설정되어 메모리 누수를 방지합니다.
src/hooks/usePopupActions.ts (3)
5-5: 타입 임포트가 적절합니다.ReplyModalProps 타입이 올바르게 임포트되었습니다.
23-26: 댓글 모달 액션 함수가 일관된 패턴으로 구현되었습니다.기존 팝업 액션들과 동일한 패턴을 따라 구현되어 코드 일관성이 유지되었습니다.
32-32: 반환 객체에 새로운 함수가 올바르게 추가되었습니다.openReplyModal이 적절히 노출되어 다른 컴포넌트에서 사용할 수 있습니다.
src/components/common/Modal/PopupContainer.tsx (3)
6-6: ReplyModal 컴포넌트 임포트가 적절합니다.
8-13: 타입 임포트가 올바르게 추가되었습니다.ReplyModalProps 타입이 적절히 임포트되어 타입 안전성이 보장됩니다.
47-49: ReplyModal 자체 위치 제어 방식 검증 완료ReplyModal 컴포넌트 내부에서 아래와 같이
Overlay및ModalContainer에position: fixed를 적용하고, 전달된positionprop을 기반으로left/top값을 계산하고 있습니다.
따라서 별도의 외부 Wrapper 없이 렌더링하는 것은 의도된 설계이며, 추가 수정은 필요 없습니다.src/components/feed/UserProfileItem.tsx (4)
11-11: prop 명명 변경이 일관되게 적용되었습니다.타입 정의와 일치하게 nickname으로 변경되었습니다.
34-34: 콘솔 로깅에서도 업데이트된 prop명이 올바르게 사용되었습니다.
46-46: JSX에서 nickname prop이 올바르게 사용되었습니다.
70-72: 반응형 제약 조건이 적절히 추가되었습니다.max-width, min-width, margin: 0 auto 스타일 추가로 다양한 화면 크기에서의 일관된 레이아웃이 보장됩니다.
src/components/common/Post/ReplyList.tsx (1)
16-17: 속성명 변경이 올바르게 적용되었습니다.
replyCommentList→replyList,replyCommentId→replyId로 변경된 것이 타입 정의와 일관성 있게 적용되어 코드의 가독성이 향상되었습니다.src/pages/feed/UserSearch.tsx (2)
11-11: useUserSearch 훅 통합이 잘 구현되었습니다.정적 데이터를 동적 검색으로 교체하여 실제 API와 연동된 사용자 검색 기능을 제공합니다. 디바운스 지연 300ms와 페이지 크기 20은 적절한 설정입니다.
Also applies to: 19-23
92-96: 무한 스크롤을 위한 props 전달이 적절합니다.UserSearchResult 컴포넌트에
loading,hasMore,onLoadMoreprops를 전달하여 무한 스크롤 기능이 올바르게 구현되었습니다. 검색 중(searching)과 검색 완료(searched) 상태 모두에서 동일한 데이터를 사용하는 것이 일관성 있습니다.Also applies to: 100-104
src/pages/feed/FeedDetailPage.tsx (4)
13-13: 댓글 API 통합이 적절하게 구현되었습니다.
getCommentsAPI를 import하고commentList상태를 추가하여 동적 댓글 로딩을 지원합니다. 타입 안전성도CommentData[]로 보장되어 있습니다.Also applies to: 19-19
27-32: 페이지 언마운트 시 답글 상태 정리가 잘 구현되었습니다.컴포넌트가 언마운트될 때
cancelReply()를 호출하여 답글 작성 상태를 초기화하는 것은 메모리 누수 방지와 사용자 경험 개선에 도움이 됩니다.
34-64: Promise.all을 사용한 병렬 데이터 로딩이 효율적입니다.피드 상세 정보와 댓글 목록을 병렬로 로드하여 성능을 최적화했습니다. 에러 처리도 두 API 호출을 함께 관리하여 일관된 사용자 경험을 제공합니다.
128-128: 동적 댓글 리스트 연동이 완료되었습니다.정적 mock 데이터 대신 API로부터 로드된
commentList를 사용하여 실시간 댓글 데이터를 표시합니다.src/data/postData.ts (2)
89-92: 댓글 데이터 구조의 네이밍 통일이 잘 되었습니다.
userId→creatorId,imageUrl→creatorProfileImageUrl등 일관된 네이밍 컨벤션으로 변경하여 API 응답 구조와 일치시켰습니다. 코드 가독성과 유지보수성이 향상되었습니다.
99-99: 답글 데이터 구조도 일관되게 리팩토링되었습니다.
replyCommentList→replyList,replyCommentId→replyId등 답글 관련 속성들이 메인 댓글과 동일한 패턴으로 변경되어 데이터 구조의 일관성이 확보되었습니다.Also applies to: 101-108, 113-120
src/stores/usePopupStore.ts (3)
4-4: PopupType에 'reply-modal' 추가가 적절합니다.기존 팝업 시스템을 확장하여 답글 모달 지원을 추가했습니다. 타입 안전성을 유지하면서 깔끔하게 확장되었습니다.
28-37: ReplyModalProps 인터페이스 설계가 잘 되었습니다.
userId,replyId등 필수 정보와 선택적position속성을 포함하여 답글 모달의 다양한 사용 사례를 지원합니다. 특히 position 속성으로 클릭한 위치에 맞춰 모달을 배치할 수 있어 UX가 향상됩니다.
42-42: 기존 팝업 시스템과의 통합이 일관성 있게 구현되었습니다.
PopupState인터페이스와openPopup메서드가 새로운 ReplyModalProps를 포함하도록 확장되었으며, 기존 코드에 영향을 주지 않으면서 타입 안전성을 보장합니다.Also applies to: 44-47
src/api/users/getUsers.ts (1)
3-10: 사용자 데이터 인터페이스가 잘 정의됨사용자 데이터 구조가 명확하고 일관성 있게 정의되어 있습니다.
src/components/common/Modal/ReplyModal.tsx (1)
12-24: localStorage 접근이 안전하게 처리됨try-catch 블록으로 localStorage 접근 시 발생할 수 있는 에러를 적절히 처리하고 있습니다.
src/hooks/useUserSearch.ts (4)
11-16: 상태 관리가 적절히 구성됨사용자 검색에 필요한 모든 상태가 적절히 정의되어 있습니다. 디폴트 값 설정도 합리적입니다.
19-53: 검색 함수 로직이 잘 구현됨디바운스된 키워드로 검색하고, 로딩/에러 상태를 적절히 관리하며, 무한 스크롤을 위한 결과 병합 로직이 잘 구현되어 있습니다.
44-44: 무한 스크롤 판단 로직의 한계점 인지 필요PR 목표에서 언급된 것처럼, 반환된 데이터 길이가 요청 크기와 정확히 일치할 때 더 많은 데이터가 있는지 판단하기 어려운 문제가 있습니다. 현재 구현은 서버에서 페이지네이션을 명시적으로 지원하지 않는 상황에서의 최선의 접근 방식입니다.
향후 서버 API가 총 데이터 수나 hasMore 플래그를 제공하도록 개선될 예정인지 확인해보세요.
60-64: loadMore 함수가 안전하게 구현됨로딩 중이거나 더 이상 데이터가 없을 때, 또는 검색어가 없을 때 중복 요청을 방지하는 로직이 잘 구현되어 있습니다.
src/components/common/Post/Reply.tsx (1)
37-38: 좋아요 수 업데이트 로직의 정확성을 검증하세요.현재 로직은 단순히 +1/-1을 하고 있는데, 이는 동시성 문제나 네트워크 지연 시 실제 서버 상태와 불일치할 수 있습니다.
서버 응답에서 정확한 좋아요 수를 반환하는지 확인하고, 가능하다면 서버에서 반환한 값을 직접 사용하는 것이 좋습니다:
- setLikeCount(prev => (response.data.isLiked ? prev + 1 : prev - 1)); + // 서버에서 정확한 likeCount를 반환한다면: + // setLikeCount(response.data.likeCount);src/types/post.ts (1)
42-68: 타입 정의가 일관성 있게 잘 개선되었습니다!creator 접두사를 사용한 일관된 네이밍 컨벤션과 nullable 타입 처리가 적절합니다.
| export const deleteComment = async (commentId: number) => { | ||
| const response = await apiClient.delete<DeleteCommentResponse>(`/comments/${commentId}`, { | ||
| data: { commentId }, | ||
| }); | ||
| return response.data; | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
HTTP DELETE 요청 방식 개선 및 반환 타입 명시 필요
- DELETE 요청에서 body에 데이터를 보내는 것은 HTTP 표준에 맞지 않습니다. 경로 매개변수에 이미 commentId가 있으므로 body는 불필요합니다.
- 함수 반환 타입이 명시되지 않았습니다.
다음과 같이 수정하세요:
-export const deleteComment = async (commentId: number) => {
- const response = await apiClient.delete<DeleteCommentResponse>(`/comments/${commentId}`, {
- data: { commentId },
- });
+export const deleteComment = async (commentId: number): Promise<DeleteCommentResponse> => {
+ const response = await apiClient.delete<DeleteCommentResponse>(`/comments/${commentId}`);
return response.data;
};🤖 Prompt for AI Agents
In src/api/comments/deleteComment.ts lines 16 to 21, the deleteComment function
sends commentId in the request body of a DELETE request, which is against HTTP
standards since the ID is already in the URL path. Remove the data property from
the request options. Also, explicitly declare the function's return type to
match the expected response data type for clarity and type safety.
| export const getComments = async (postId: number, params?: GetCommentsParams) => { | ||
| const searchParams = new URLSearchParams(); | ||
|
|
||
| if (params?.size) { | ||
| searchParams.append('size', params.size.toString()); | ||
| } | ||
|
|
||
| if (params?.cursor) { | ||
| searchParams.append('cursor', params.cursor); | ||
| } | ||
|
|
||
| const queryString = searchParams.toString(); | ||
| const url = queryString ? `/comments/${postId}?${queryString}` : `/comments/${postId}`; | ||
|
|
||
| const response = await apiClient.get<GetCommentsResponse>(url); | ||
| return response.data; | ||
| }; |
There was a problem hiding this comment.
postId 유효성 검증 및 에러 처리를 추가하세요.
postId 파라미터 검증과 API 호출 실패에 대한 에러 처리가 누락되어 있습니다.
다음과 같이 개선하세요:
export const getComments = async (postId: number, params?: GetCommentsParams) => {
+ if (!postId || postId <= 0) {
+ throw new Error('유효하지 않은 postId입니다');
+ }
+
const searchParams = new URLSearchParams();
if (params?.size) {
searchParams.append('size', params.size.toString());
}
if (params?.cursor) {
searchParams.append('cursor', params.cursor);
}
const queryString = searchParams.toString();
const url = queryString ? `/comments/${postId}?${queryString}` : `/comments/${postId}`;
- const response = await apiClient.get<GetCommentsResponse>(url);
- return response.data;
+ try {
+ const response = await apiClient.get<GetCommentsResponse>(url);
+ return response.data;
+ } catch (error) {
+ console.error('댓글 조회 실패:', error);
+ throw error;
+ }
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const getComments = async (postId: number, params?: GetCommentsParams) => { | |
| const searchParams = new URLSearchParams(); | |
| if (params?.size) { | |
| searchParams.append('size', params.size.toString()); | |
| } | |
| if (params?.cursor) { | |
| searchParams.append('cursor', params.cursor); | |
| } | |
| const queryString = searchParams.toString(); | |
| const url = queryString ? `/comments/${postId}?${queryString}` : `/comments/${postId}`; | |
| const response = await apiClient.get<GetCommentsResponse>(url); | |
| return response.data; | |
| }; | |
| export const getComments = async (postId: number, params?: GetCommentsParams) => { | |
| if (!postId || postId <= 0) { | |
| throw new Error('유효하지 않은 postId입니다'); | |
| } | |
| const searchParams = new URLSearchParams(); | |
| if (params?.size) { | |
| searchParams.append('size', params.size.toString()); | |
| } | |
| if (params?.cursor) { | |
| searchParams.append('cursor', params.cursor); | |
| } | |
| const queryString = searchParams.toString(); | |
| const url = queryString | |
| ? `/comments/${postId}?${queryString}` | |
| : `/comments/${postId}`; | |
| try { | |
| const response = await apiClient.get<GetCommentsResponse>(url); | |
| return response.data; | |
| } catch (error) { | |
| console.error('댓글 조회 실패:', error); | |
| throw error; | |
| } | |
| }; |
🤖 Prompt for AI Agents
In src/api/comments/getComments.ts around lines 46 to 62, the function lacks
validation for the postId parameter and does not handle errors from the API
call. Add a check at the start of the function to ensure postId is a valid
positive number and throw an error if not. Wrap the API call in a try-catch
block to catch any request failures and handle or rethrow the error
appropriately to improve robustness.
| export const postLike = async (commentId: number, type: boolean) => { | ||
| const response = await apiClient.post<PostLikeResponse>(`/comments/${commentId}/likes`, { | ||
| type, | ||
| }); | ||
| return response.data; | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
함수 반환 타입 명시와 에러 처리 개선 필요
현재 함수가 response.data를 반환하지만 명시적인 반환 타입이 없고, API 호출 실패 시 에러 처리가 없습니다.
다음 개선사항을 적용하세요:
-export const postLike = async (commentId: number, type: boolean) => {
+export const postLike = async (commentId: number, type: boolean): Promise<PostLikeResponse["data"]> => {
const response = await apiClient.post<PostLikeResponse>(`/comments/${commentId}/likes`, {
type,
});
return response.data;
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const postLike = async (commentId: number, type: boolean) => { | |
| const response = await apiClient.post<PostLikeResponse>(`/comments/${commentId}/likes`, { | |
| type, | |
| }); | |
| return response.data; | |
| }; | |
| export const postLike = async (commentId: number, type: boolean): Promise<PostLikeResponse["data"]> => { | |
| const response = await apiClient.post<PostLikeResponse>(`/comments/${commentId}/likes`, { | |
| type, | |
| }); | |
| return response.data; | |
| }; |
🤖 Prompt for AI Agents
In src/api/comments/postLike.ts around lines 17 to 22, the postLike function
lacks an explicit return type and does not handle errors from the API call. To
fix this, explicitly declare the function's return type as a Promise of
PostLikeResponse and wrap the API call in a try-catch block to catch and handle
any errors, such as logging or rethrowing them appropriately.
| export const getUsers = async (params?: GetUsersParams) => { | ||
| const searchParams = new URLSearchParams(); | ||
|
|
||
| if (params?.keyword) { | ||
| searchParams.append('keyword', params.keyword); | ||
| } | ||
|
|
||
| if (params?.size) { | ||
| searchParams.append('size', params.size.toString()); | ||
| } | ||
|
|
||
| const queryString = searchParams.toString(); | ||
| const url = queryString ? `/users?${queryString}` : '/users'; | ||
|
|
||
| const response = await apiClient.get<GetUsersResponse>(url); | ||
| return response.data; | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
함수 반환 타입 명시 필요
함수의 반환 타입이 명시되지 않았습니다. TypeScript 모범 사례에 따라 명시적인 반환 타입을 추가하세요.
-export const getUsers = async (params?: GetUsersParams) => {
+export const getUsers = async (params?: GetUsersParams): Promise<GetUsersResponse> => {🤖 Prompt for AI Agents
In src/api/users/getUsers.ts between lines 26 and 42, the getUsers function
lacks an explicit return type. Add a TypeScript return type annotation to the
function signature that matches the expected resolved type of the promise, such
as Promise<GetUsersResponse>, to clearly specify what the function returns.
| {isMyReply ? ( | ||
| <OptionItem className="report"> | ||
| <div className="option-text">신고하기</div> | ||
| </OptionItem> | ||
| ) : ( | ||
| <OptionItem onClick={handleDelete} className="delete"> | ||
| <div className="option-text">삭제하기</div> | ||
| </OptionItem> | ||
| )} |
There was a problem hiding this comment.
조건문 로직 오류 - 즉시 수정 필요
조건문의 로직이 반대로 구현되어 있습니다. 내 댓글인 경우 "삭제하기"가 표시되어야 하는데 "신고하기"가 표시되고, 다른 사람 댓글인 경우 "신고하기"가 표시되어야 하는데 "삭제하기"가 표시됩니다.
다음과 같이 수정하세요:
- {isMyReply ? (
- <OptionItem className="report">
- <div className="option-text">신고하기</div>
- </OptionItem>
- ) : (
+ {isMyReply ? (
<OptionItem onClick={handleDelete} className="delete">
<div className="option-text">삭제하기</div>
</OptionItem>
+ ) : (
+ <OptionItem className="report">
+ <div className="option-text">신고하기</div>
+ </OptionItem>
)}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {isMyReply ? ( | |
| <OptionItem className="report"> | |
| <div className="option-text">신고하기</div> | |
| </OptionItem> | |
| ) : ( | |
| <OptionItem onClick={handleDelete} className="delete"> | |
| <div className="option-text">삭제하기</div> | |
| </OptionItem> | |
| )} | |
| {isMyReply ? ( | |
| <OptionItem onClick={handleDelete} className="delete"> | |
| <div className="option-text">삭제하기</div> | |
| </OptionItem> | |
| ) : ( | |
| <OptionItem className="report"> | |
| <div className="option-text">신고하기</div> | |
| </OptionItem> | |
| )} |
🤖 Prompt for AI Agents
In src/components/common/Modal/ReplyModal.tsx between lines 79 and 87, the
conditional rendering logic is reversed: when isMyReply is true, it incorrectly
shows "신고하기" instead of "삭제하기", and vice versa. Swap the JSX blocks inside the
ternary operator so that when isMyReply is true, the "삭제하기" OptionItem with
onClick={handleDelete} is rendered, and when false, the "신고하기" OptionItem
without onClick is rendered.
| console.log('좋아요 상태 변경 성공:', response); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
프로덕션 코드에서 console.log를 제거하세요.
디버깅용 console.log가 프로덕션 코드에 남아있습니다.
다음과 같이 제거하거나 적절한 로깅 시스템으로 대체하세요:
const handleLike = async () => {
try {
const response = await postLike(commentId, !liked);
- console.log('좋아요 상태 변경 성공:', response);
// 서버 응답으로 상태 업데이트
setLiked(response.data.isLiked);
setLikeCount(prev => (response.data.isLiked ? prev + 1 : prev - 1));
} catch (error) {
- console.error('좋아요 상태 변경 실패:', error);
+ // 적절한 에러 처리 (예: 토스트 메시지 표시)
}
};Also applies to: 40-41
🤖 Prompt for AI Agents
In src/components/common/Post/Reply.tsx at lines 34-35 and 40-41, remove the
console.log statements used for debugging the like status change success. These
logs should be deleted or replaced with a proper logging system if necessary to
avoid cluttering production code with console outputs.
| console.log('좋아요 상태 변경 성공:', response); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
프로덕션 코드에서 console.log를 제거하세요.
디버깅용 console.log가 프로덕션 코드에 남아있습니다.
Reply.tsx와 동일한 문제입니다. 다음과 같이 제거하세요:
const handleLike = async () => {
try {
const response = await postLike(replyId, !liked);
- console.log('좋아요 상태 변경 성공:', response);
// 서버 응답으로 상태 업데이트
setLiked(response.data.isLiked);
setCurrentLikeCount(prev => (response.data.isLiked ? prev + 1 : prev - 1));
} catch (error) {
- console.error('좋아요 상태 변경 실패:', error);
+ // 적절한 에러 처리 (예: 토스트 메시지 표시)
}
};Also applies to: 46-47
🤖 Prompt for AI Agents
In src/components/common/Post/SubReply.tsx at lines 40-41 and 46-47, remove the
console.log statements used for debugging the like status change success. These
console.log calls should not be present in production code. Simply delete these
lines to clean up the code.
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (6)
src/api/comments/postLike.ts (1)
17-22: 에러 핸들링이 필요합니다.API 호출 실패 시 적절한 에러 처리를 추가하는 것을 고려해보세요.
export const postLike = async (commentId: number, type: boolean) => { - const response = await apiClient.post<PostLikeResponse>(`/comments/${commentId}/likes`, { - type, - }); - return response.data; + try { + const response = await apiClient.post<PostLikeResponse>(`/comments/${commentId}/likes`, { + type, + }); + return response.data; + } catch (error) { + console.error('좋아요 처리 실패:', error); + throw error; + } };src/api/comments/deleteComment.ts (1)
16-21: API 호출 방식 검토 필요URL 패스와 요청 본문에 모두 commentId를 포함하고 있습니다. 일반적으로 DELETE 요청에서는 URL 패스의 ID만으로 충분하므로 요청 본문의 commentId가 필요한지 확인이 필요합니다.
요청 본문이 불필요하다면 다음과 같이 단순화할 수 있습니다:
export const deleteComment = async (commentId: number) => { - const response = await apiClient.delete<DeleteCommentResponse>(`/comments/${commentId}`, { - data: { commentId }, - }); + const response = await apiClient.delete<DeleteCommentResponse>(`/comments/${commentId}`); return response.data; };src/pages/feed/UserSearchResult.tsx (2)
22-25: 불필요하게 복잡한 함수 단순화
isEmptySearchedUserList함수를 더 간단하게 작성할 수 있습니다.const isEmptySearchedUserList = () => { - if (searchedUserList.length === 0) return true; - else return false; + return searchedUserList.length === 0; };
30-45: Intersection Observer 의존성 최적화 필요
onLoadMore함수가 부모 컴포넌트에서 매번 재생성될 경우, 불필요한 Observer 재연결이 발생할 수 있습니다. 부모 컴포넌트에서useCallback으로 메모이제이션하거나, 의존성 배열을 최적화해야 합니다.부모 컴포넌트에서
onLoadMore를 다음과 같이 메모이제이션하세요:const onLoadMore = useCallback(() => { // 더 많은 데이터 로드 로직 }, [/* 필요한 의존성 */]);src/components/common/Post/SubReply.tsx (2)
73-73: null 프로필 이미지 처리 개선 필요
creatorProfileImageUrl이 null일 때 빈 문자열 대신undefined를 전달하는 것이 더 적절합니다.- creatorProfileImageUrl={creatorProfileImageUrl || ''} + creatorProfileImageUrl={creatorProfileImageUrl || undefined}
146-151: 중복된 스타일 제거 가능
.reply-nickname스타일이 부모.reply스타일과 동일합니다. 별도 스타일이 필요하지 않다면 제거할 수 있습니다..reply-nickname { - color: ${colors.grey[100]}; - font-size: ${typography.fontSize.sm}; - font-weight: ${typography.fontWeight.regular}; - line-height: 20px; + display: inline; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
src/assets/header/header-logo.svgis excluded by!**/*.svg
📒 Files selected for processing (22)
index.html(1 hunks)src/api/comments/deleteComment.ts(1 hunks)src/api/comments/getComments.ts(1 hunks)src/api/comments/postLike.ts(1 hunks)src/api/users/getUsers.ts(1 hunks)src/components/common/Modal/PopupContainer.tsx(2 hunks)src/components/common/Modal/ReplyModal.tsx(1 hunks)src/components/common/Post/Reply.tsx(2 hunks)src/components/common/Post/ReplyList.tsx(1 hunks)src/components/common/Post/SubReply.tsx(3 hunks)src/components/feed/UserProfileItem.tsx(4 hunks)src/data/postData.ts(1 hunks)src/hooks/useDebounce.ts(1 hunks)src/hooks/usePopupActions.ts(2 hunks)src/hooks/useUserSearch.ts(1 hunks)src/pages/feed/FeedDetailPage.tsx(3 hunks)src/pages/feed/UserSearch.tsx(2 hunks)src/pages/feed/UserSearchResult.tsx(3 hunks)src/stores/usePopupStore.ts(2 hunks)src/types/post.ts(1 hunks)src/types/profile.ts(0 hunks)src/types/user.ts(1 hunks)
💤 Files with no reviewable changes (1)
- src/types/profile.ts
🧰 Additional context used
🧬 Code Graph Analysis (10)
src/hooks/usePopupActions.ts (1)
src/stores/usePopupStore.ts (1)
ReplyModalProps(28-37)
src/components/common/Modal/PopupContainer.tsx (1)
src/stores/usePopupStore.ts (1)
ReplyModalProps(28-37)
src/api/comments/deleteComment.ts (1)
src/api/index.ts (1)
apiClient(27-33)
src/pages/feed/UserSearch.tsx (2)
src/hooks/useUserSearch.ts (1)
useUserSearch(11-80)src/pages/feed/UserSearchResult.tsx (1)
UserSearchResult(15-70)
src/hooks/useUserSearch.ts (2)
src/api/users/getUsers.ts (2)
UserData(3-10)getUsers(26-42)src/hooks/useDebounce.ts (1)
useDebounce(3-17)
src/components/common/Post/Reply.tsx (3)
src/hooks/useReplyActions.ts (1)
useReplyActions(10-54)src/hooks/usePopupActions.ts (1)
usePopupActions(9-35)src/api/comments/postLike.ts (1)
postLike(17-22)
src/api/comments/getComments.ts (1)
src/api/index.ts (1)
apiClient(27-33)
src/api/comments/postLike.ts (1)
src/api/index.ts (1)
apiClient(27-33)
src/pages/feed/UserSearchResult.tsx (1)
src/api/users/getUsers.ts (1)
UserData(3-10)
src/api/users/getUsers.ts (1)
src/api/index.ts (1)
apiClient(27-33)
🔇 Additional comments (36)
index.html (1)
5-5: 파비콘 경로를 절대 경로로 변경한 점 확인 필요
href="/assets/custom_favicon.svg"로 절대 경로를 사용하면 루트 도메인(/)가 배포 base path와 동일할 때만 정상 동작합니다.
만약 프리뷰·스토리북·서브 디렉터리 배포(예:https://example.com/app/)가 존재한다면 파비콘이 로드되지 않을 수 있으니, 다음 중 하나를 검토해 주세요.- <link rel="icon" type="image/svg+xml" href="/assets/custom_favicon.svg" /> + <!-- 1) 상대 경로로 유지 --> + <link rel="icon" type="image/svg+xml" href="./assets/custom_favicon.svg" /> + + <!-- 2) Vite 환경 변수(base URL)를 활용 --> + <link rel="icon" type="image/svg+xml" href="%VITE_BASE_URL%assets/custom_favicon.svg" />배포 환경이 항상 루트에 고정되어 있는지 확인 부탁드립니다.
src/types/user.ts (1)
5-5: 네이밍 컨벤션 개선이 적절합니다.
nickName을nickname으로 변경하여 camelCase 네이밍 컨벤션을 일관되게 적용한 점이 좋습니다.src/components/feed/UserProfileItem.tsx (2)
11-11: 프로퍼티명 변경이 타입 정의와 일치합니다.
nickName을nickname으로 변경하여 타입 정의와 일관성을 유지한 점이 좋습니다.
70-72: 반응형 디자인 개선이 적절합니다.
max-width,min-width,margin: 0 auto추가로 반응형 레이아웃과 중앙 정렬이 잘 구현되었습니다.src/hooks/useDebounce.ts (1)
1-17: 디바운스 훅 구현이 우수합니다.제네릭 타입을 지원하며, cleanup 함수로 메모리 누수를 방지하는 표준적인 디바운스 로직이 잘 구현되어 있습니다. 사용자 검색 API 호출 최적화에 유용할 것입니다.
src/components/common/Modal/PopupContainer.tsx (3)
6-6: ReplyModal 임포트가 적절합니다.새로운 모달 컴포넌트 임포트가 올바르게 추가되었습니다.
8-13: 타입 임포트가 일관되게 구현되었습니다.ReplyModalProps 타입 임포트가 기존 패턴과 일관되게 추가되었습니다.
47-49: Wrapper 사용 여부를 확인해주세요.다른 모달들과 달리 ReplyModal은 Wrapper를 사용하지 않습니다. 이것이 의도된 디자인인지 확인해주세요. ReplyModal이 자체적으로 포지셔닝을 처리하는 것인지 검토가 필요합니다.
src/hooks/usePopupActions.ts (3)
1-6: ReplyModalProps import 추가 승인댓글 더보기 모달 기능을 위한 타입 import가 적절히 추가되었습니다.
23-26: openReplyModal 함수 구현 승인기존 패턴과 일관성 있게 구현되었으며, 댓글 더보기 모달 기능이 올바르게 추가되었습니다.
32-32: 반환 객체에 openReplyModal 추가 승인새로운 함수가 올바르게 export되어 다른 컴포넌트에서 사용할 수 있도록 구성되었습니다.
src/components/common/Post/ReplyList.tsx (1)
16-17: 데이터 구조 변경에 따른 프로퍼티명 업데이트 승인
replyCommentList→replyList,replyCommentId→replyId로의 변경이 전체 데이터 모델 리팩토링과 일관성 있게 적용되었습니다.src/pages/feed/UserSearch.tsx (4)
11-11: useUserSearch hook import 승인실시간 사용자 검색 기능을 위한 hook이 적절히 import되었습니다.
19-23: useUserSearch hook 통합 승인정적 mock 데이터에서 동적 API 데이터로의 전환이 올바르게 구현되었습니다. 디바운싱, 페이지네이션, 로딩 상태 관리가 잘 통합되어 있습니다.
92-96: UserSearchResult props 업데이트 승인 (searched 상태)무한 스크롤 기능을 위한 새로운 props(
loading,hasMore,onLoadMore)가 올바르게 전달되었습니다.
100-104: UserSearchResult props 업데이트 승인 (searching 상태)검색 중 상태에서도 동일한 props가 일관성 있게 전달되어 무한 스크롤 기능이 지원됩니다.
src/pages/feed/FeedDetailPage.tsx (5)
13-13: getComments API import 승인댓글 데이터를 동적으로 가져오기 위한 API가 적절히 import되었습니다.
19-19: commentList 상태 추가 승인댓글 데이터를 관리하기 위한 상태가 올바르게 추가되었습니다.
28-32: cleanup effect 추가 승인페이지를 떠날 때 답글 상태를 초기화하는 적절한 cleanup 로직이 추가되었습니다.
35-64: 병렬 데이터 로딩 구현 승인Promise.all을 사용하여 피드 상세 정보와 댓글 목록을 효율적으로 병렬 로딩하는 구현이 훌륭합니다. 에러 처리와 로딩 상태 관리도 적절합니다.
128-128: ReplyList에 동적 댓글 데이터 전달 승인정적 mock 데이터 대신 API에서 가져온 동적 댓글 데이터가 올바르게 전달되었습니다.
src/data/postData.ts (5)
89-93: 댓글 작성자 프로퍼티명 리팩토링 승인댓글 작성자 관련 프로퍼티명이 일관된 명명 규칙(
creator*)으로 통일되어 코드 가독성과 일관성이 향상되었습니다.
99-99: 답글 목록 프로퍼티명 변경 승인
replyCommentList→replyList로의 변경이 다른 컴포넌트들의 변경사항과 일관성 있게 적용되었습니다.
101-109: 첫 번째 답글 프로퍼티명 리팩토링 승인답글 관련 프로퍼티들이 간결하고 일관된 명명 규칙으로 개선되었습니다 (
replyCommentId→replyId,replyCommentContent→content등).
113-121: 두 번째 답글 프로퍼티명 리팩토링 승인동일한 명명 규칙이 일관성 있게 적용되어 전체 데이터 구조의 통일성이 확보되었습니다.
128-137: 두 번째 댓글 프로퍼티명 리팩토링 승인모든 댓글에서 동일한 명명 규칙이 적용되어 데이터 구조의 일관성이 완벽하게 유지되었습니다.
src/hooks/useUserSearch.ts (3)
1-9: 임포트와 타입 정의가 적절함필요한 의존성들이 올바르게 임포트되어 있고 인터페이스 정의도 명확합니다.
44-44: 페이지네이션 로직의 한계점 확인 필요현재
setHasMore(newUserList.length === size)로직은 PR 목표에서 언급된 것처럼, 정확히 size와 일치할 때 더 많은 데이터가 있는지 판단하기 어려운 한계가 있습니다. 서버에서 명시적인 hasMore 필드를 제공하지 않는 한 이는 불가피한 제약사항으로 보입니다.서버 API 응답에서 명시적으로 hasMore나 totalCount 같은 필드를 제공하는지 확인해보세요.
60-64: loadMore 함수의 조건 검사가 적절함로딩 상태, hasMore 플래그, 키워드 존재 여부를 모두 체크하여 불필요한 API 호출을 방지하고 있습니다.
src/api/users/getUsers.ts (2)
3-10: UserData 인터페이스 정의가 명확함사용자 데이터 구조가 잘 정의되어 있고 필드명도 직관적입니다.
26-42: 쿼리 파라미터 구성 로직이 견고함URLSearchParams를 사용한 쿼리 스트링 구성이 안전하고, 선택적 파라미터 처리도 적절합니다.
src/components/common/Modal/ReplyModal.tsx (1)
12-24: localStorage 접근에 대한 에러 핸들링이 적절함try-catch 블록으로 localStorage 접근 오류를 적절히 처리하고 있습니다.
src/api/comments/getComments.ts (2)
3-28: 댓글과 답글 인터페이스 구조가 일관됨CommentData와 ReplyData가 거의 동일한 필드를 가지고 있어 일관성이 있습니다. 공통 인터페이스를 추출하여 코드 중복을 줄일 수도 있지만, 향후 각각 다른 필드가 추가될 가능성을 고려하면 현재 구조도 적절합니다.
46-62: 커서 기반 페이지네이션 구현이 적절함선택적 파라미터로 size와 cursor를 받아 쿼리 스트링을 구성하는 방식이 깔끔하고 안전합니다.
src/stores/usePopupStore.ts (1)
4-37: 팝업 타입 확장이 적절하게 구현됨새로운
reply-modal타입과ReplyModalProps인터페이스가 기존 패턴을 잘 따르고 있으며, 필요한 속성들이 모두 포함되어 있습니다.src/types/post.ts (1)
41-68: 타입 정의 개선이 잘 이루어짐프로퍼티 명명 규칙이 일관성 있게 개선되었고, nullable 타입 처리가 적절합니다.
creatorProfileImageUrl을 nullable로 처리한 것과parentCommentCreatorNickname을 옵셔널로 추가한 것이 실제 사용 케이스를 잘 반영합니다.
| export interface PostLikeRequest { | ||
| type: boolean; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
요청 인터페이스의 프로퍼티명이 모호합니다.
type: boolean보다는 isLike: boolean 또는 action: 'like' | 'unlike'가 더 명확합니다.
export interface PostLikeRequest {
- type: boolean;
+ isLike: boolean;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export interface PostLikeRequest { | |
| type: boolean; | |
| } | |
| export interface PostLikeRequest { | |
| isLike: boolean; | |
| } |
🤖 Prompt for AI Agents
In src/api/comments/postLike.ts lines 3 to 5, the property name 'type' in the
PostLikeRequest interface is ambiguous. Rename the property to a clearer name
such as 'isLike' with a boolean type or 'action' with a union type of 'like' or
'unlike' to better convey the intent of the request.
| export interface PostLikeResponse { | ||
| code: number; | ||
| status: string; | ||
| message: string; | ||
| data: { | ||
| commentId: number; | ||
| isLiked: boolean; | ||
| }; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
응답 인터페이스에 좋아요 개수가 누락되었습니다.
UI 컴포넌트에서 좋아요 개수를 업데이트하려면 likeCount 필드가 필요할 것으로 보입니다.
export interface PostLikeResponse {
code: number;
status: string;
message: string;
data: {
commentId: number;
isLiked: boolean;
+ likeCount: number;
};
}🤖 Prompt for AI Agents
In src/api/comments/postLike.ts between lines 7 and 15, the PostLikeResponse
interface is missing the likeCount field in the data object. Add a likeCount
property of type number to the data object to provide the UI component with the
updated count of likes.
| {isMyReply ? ( | ||
| <OptionItem className="report"> | ||
| <div className="option-text">신고하기</div> | ||
| </OptionItem> | ||
| ) : ( | ||
| <OptionItem onClick={handleDelete} className="delete"> | ||
| <div className="option-text">삭제하기</div> | ||
| </OptionItem> | ||
| )} |
There was a problem hiding this comment.
삭제/신고 옵션 로직이 반대로 구현됨
현재 코드에서 isMyReply가 true일 때 "신고하기"를, false일 때 "삭제하기"를 보여주고 있는데, 이는 일반적인 UX와 반대입니다. 내 댓글은 삭제할 수 있어야 하고, 다른 사람 댓글은 신고할 수 있어야 합니다.
다음과 같이 수정해야 합니다:
- {isMyReply ? (
- <OptionItem className="report">
- <div className="option-text">신고하기</div>
- </OptionItem>
- ) : (
- <OptionItem onClick={handleDelete} className="delete">
- <div className="option-text">삭제하기</div>
- </OptionItem>
- )}
+ {isMyReply ? (
+ <OptionItem onClick={handleDelete} className="delete">
+ <div className="option-text">삭제하기</div>
+ </OptionItem>
+ ) : (
+ <OptionItem className="report">
+ <div className="option-text">신고하기</div>
+ </OptionItem>
+ )}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {isMyReply ? ( | |
| <OptionItem className="report"> | |
| <div className="option-text">신고하기</div> | |
| </OptionItem> | |
| ) : ( | |
| <OptionItem onClick={handleDelete} className="delete"> | |
| <div className="option-text">삭제하기</div> | |
| </OptionItem> | |
| )} | |
| {isMyReply ? ( | |
| <OptionItem onClick={handleDelete} className="delete"> | |
| <div className="option-text">삭제하기</div> | |
| </OptionItem> | |
| ) : ( | |
| <OptionItem className="report"> | |
| <div className="option-text">신고하기</div> | |
| </OptionItem> | |
| )} |
🤖 Prompt for AI Agents
In src/components/common/Modal/ReplyModal.tsx between lines 79 and 87, the logic
for displaying "삭제하기" and "신고하기" options is reversed. When isMyReply is true, it
should show the "삭제하기" option with the onClick handler for deletion, and when
false, it should show the "신고하기" option without the delete handler. Swap the JSX
blocks inside the conditional rendering to fix this UX issue.
| console.log('좋아요 상태 변경 성공:', response); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
프로덕션 코드에서 console.log 제거 필요
디버깅용 console.log 문은 프로덕션 환경에서 제거되어야 합니다.
- console.log('좋아요 상태 변경 성공:', response);
-- console.error('좋아요 상태 변경 실패:', error);
+ // TODO: 사용자에게 에러 알림 표시Also applies to: 40-41
🤖 Prompt for AI Agents
In src/components/common/Post/Reply.tsx at lines 34-35 and 40-41, remove the
console.log statements used for debugging the like status change success and any
other console.log calls to ensure no debug logs remain in the production code.
| setLiked(response.data.isLiked); | ||
| setLikeCount(prev => (response.data.isLiked ? prev + 1 : prev - 1)); |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
좋아요 카운트 업데이트 로직 개선 고려
현재 로직은 서버의 isLiked 값에 따라 단순히 +1/-1을 하고 있습니다. 여러 사용자가 동시에 좋아요를 누를 경우 실제 카운트와 차이가 발생할 수 있습니다. 서버에서 업데이트된 실제 카운트를 반환받아 사용하는 것이 더 정확할 것입니다.
서버 API 응답에 실제 좋아요 카운트가 포함되어 있는지 확인이 필요합니다:
🏁 Script executed:
#!/bin/bash
# postLike API 응답 타입 확인
ast-grep --pattern 'interface PostLikeResponse {
$$$
}'Length of output: 495
서버 응답에 실제 좋아요 카운트 포함 검토 요청
현재 PostLikeResponse 타입에는 isLiked만 반환되므로, 클라이언트에서 아래와 같이 로컬 prev +1/-1 로직을 사용하고 있습니다.
동시성 상황에서 실제 데이터와 불일치가 발생할 수 있으니, 서버 API가 업데이트된 전체 likeCount를 반환하도록 확장한 뒤 해당 값을 직접 사용하도록 변경하는 것을 권장합니다.
- 대상 파일:
src/components/common/Post/Reply.tsx - 대상 라인: 37–38
- setLiked(response.data.isLiked);
- setLikeCount(prev => (response.data.isLiked ? prev + 1 : prev - 1));
+ setLiked(response.data.isLiked);
+ // 서버에서 받은 최신 좋아요 카운트로 직접 설정
+ setLikeCount(response.data.likeCount);추가로, src/api/comments/postLike.ts의 PostLikeResponse.data에 likeCount: number; 필드를 포함하도록 API 계약을 업데이트해주세요.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| setLiked(response.data.isLiked); | |
| setLikeCount(prev => (response.data.isLiked ? prev + 1 : prev - 1)); | |
| setLiked(response.data.isLiked); | |
| // 서버에서 받은 최신 좋아요 카운트로 직접 설정 | |
| setLikeCount(response.data.likeCount); |
🤖 Prompt for AI Agents
In src/components/common/Post/Reply.tsx at lines 37-38, the current code updates
likeCount locally by incrementing or decrementing based on isLiked, which can
cause inconsistencies in concurrent scenarios. Update the server API to include
the actual likeCount in PostLikeResponse, then modify the code to setLikeCount
directly from response.data.likeCount instead of adjusting it locally. Also,
update the PostLikeResponse type in src/api/comments/postLike.ts to include
likeCount: number to reflect this API contract change.
| const response = await postLike(replyId, !liked); | ||
| console.log('좋아요 상태 변경 성공:', response); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
프로덕션 코드에서 console.log 제거 필요
디버깅용 console.log 문은 프로덕션 환경에서 제거되어야 합니다.
- console.log('좋아요 상태 변경 성공:', response);
-- console.error('좋아요 상태 변경 실패:', error);
+ // TODO: 사용자에게 에러 알림 표시Also applies to: 46-46
🤖 Prompt for AI Agents
In src/components/common/Post/SubReply.tsx at lines 39-40 and line 46, remove
the console.log statements used for debugging the like status change. These logs
should not be present in production code to avoid unnecessary console output and
potential performance issues.
#️⃣연관된 이슈
#75 comments api 연동
📝작업 내용
댓글 조회에서 부모 댓글이 무엇인지, 답글을 단 대상이 어떤닉네임인지만 잘 명시하도록 했습니다. 해당 부분 API 완성되는대로 마무리해서 수정하겠습니다.
postType만 잘 맞춰서 모임방 투표 혹은 모임방 기록에서 useReplyActions hook 호출하셔서 사용하면됩니다. (@ljh130334)
(1) 일반 댓글 작성 로직
MessageInput에서 직접 입력 -> submitComment 호출 -> postReply API 호출 -> isReplyRequest: false, parentId: null 상태 저장
-> 서버에 댓글 저장 -> 입력창 초기화
(2) 답글 작성 로직
Reply.tsx에서 "답글작성" 클릭 -> startReply(nickName, commentId) 호출 -> useReplyStore에서 전역 상태 업데이트
-> MessageInput에서 답글 UI 표시 -> submitComment 호출 -> postReply API 호출 -> isReplyRequest: true, parentId: commentId
-> 서버에 답글 저장 -> 입력창 초기화, 답글 상태 해제
스크린샷
댓글 조회 API 연동 완료되는대로 업로드하겠습니다.
💬리뷰 요구사항
특별히 없습니다.
Summary by CodeRabbit
신규 기능
버그 수정 및 개선
스타일 및 UI 개선
기타