Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Walkthrough주석, 문서화 블록 및 개발 로그를 제거하는 대규모 정리 작업입니다. 일부 API 함수의 폴백 메커니즘(multipart/form-data), 타입 확장(profileImageUrl, joinedAt 등), 오류 처리 개선사항도 포함됩니다. Changes
Sequence DiagramsequenceDiagram
participant User
participant MemoryAddButton
participant API as API (getAiUsage)
participant Modal
participant Page as AI Write Page
User->>MemoryAddButton: Click "AI 작성"
MemoryAddButton->>API: Check AI Usage & Records
alt recordCount < 2
API-->>MemoryAddButton: Insufficient records
MemoryAddButton->>User: Show error snackbar
else recordReviewCount >= 5
API-->>MemoryAddButton: Limit exceeded
MemoryAddButton->>User: Show error snackbar
else Eligible
API-->>MemoryAddButton: Usage OK
MemoryAddButton->>Modal: Open confirmation modal
User->>Modal: Confirm
Modal->>Page: Navigate to AI write
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 주의가 필요한 영역:
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/api/rooms/getBookPage.ts (1)
17-25: 반환값 불일치로 인한 호출 에러 수정 필요검증 결과, 원래 리뷰 지적이 정확합니다. 함수 구현에 중대한 오류가 있습니다:
- 함수 시그니처:
Promise<BookPageResponse>선언 (20행)- 실제 반환값:
response.data(20행)- 호출자들의 기대값:
RecordWrite.tsx(65-66행),PollWrite.tsx(76-77행)에서 모두response.isSuccess접근현재 코드는 호출자들을 런타임 에러로 이끕니다. 호출자들이
response.isSuccess를 접근할 때 해당 속성이 없어 실패합니다.수정 방안:
전체 응답을 반환하도록 변경:
-export const getBookPage = async (roomId: number): Promise<BookPageResponse> => { +export const getBookPage = async (roomId: number): Promise<BookPageResponse> => { try { const response = await apiClient.get<BookPageResponse>(`/rooms/${roomId}/book-page`); - return response.data; + return response.data; // response.data는 BookPageResponse 구조: { isSuccess, code, message, data: BookPageData } } catch (error) { console.error('책 페이지 정보 조회 API 오류:', error); throw error; } };또는 반환 타입을
BookPageData로 변경하고 모든 호출자 코드도 함께 수정해야 합니다.
🧹 Nitpick comments (14)
src/pages/memory/Memory.styled.ts (1)
23-45: ScrollableContent 주석 제거는 안전해 보입니다
padding-bottom: 100px;및 스크롤바 스타일 등 실제 스타일 값/구조는 변경되지 않아 레이아웃과 동작에는 영향이 없어 보입니다.- 이전 주석(하단 버튼 공간 확보, 스크롤바 스타일링)은 코드만 봐도 용도가 충분히 추론 가능해서, 이번 PR 목적(불필요한 주석 정리)에 잘 부합한다고 판단됩니다.
추가로, 별도 작업 기회가 있으면
100px를 디자인 토큰/공통 spacing 값으로 추출하는 정도만 고려해 볼 수 있겠습니다(이번 PR에서 필수는 아님).src/pages/groupMembers/GroupMembers.tsx (1)
42-53: 오류 처리 로직은 적절하나, 빈 줄 정리 및 콘솔 로그 검토 필요코드 레벨에서는 문제가 없습니다. 권한 없음 오류에 대한 특정 처리(
/group로 네비게이션)는 좋은 방식입니다. 다만:
- 43번 줄:
console.error호출이 프로덕션 환경에서 적절한지 검토해주세요. 프로덕션에서 에러 로깅이 필요하다면 구조화된 로거(예: Sentry 등)로 마이그레이션하는 것을 추천합니다.- 44번, 49번 줄: 주석 제거 후 남은 빈 줄입니다. 가독성상 불필요한 빈 줄을 제거하거나 한 줄만 유지하는 것을 고려하세요.
다음과 같이 정리할 수 있습니다:
try { setLoading(true); const response: RoomMembersResponse = await getRoomMembers(parseInt(currentRoomId)); if (response.isSuccess) { const convertedMembers = convertRoomMembersToMembers(response.data.userList); setMembers(convertedMembers); } else { setError(response.message); } } catch (err: unknown) { console.error('독서메이트 조회 오류:', err); - if (err instanceof Error && err.message === '방 접근 권한이 없습니다.') { navigate('/group', { replace: true }); return; } - setError('독서메이트 목록을 불러오는 중 오류가 발생했습니다.'); } finally { setLoading(false); }src/components/memory/MemoryAddButton/MemoryAddButton.tsx (1)
41-41: 매직 넘버 '1' 중복 사용 개선 권장
roomId || '1'패턴이 세 군데에서 반복됩니다. 기본값을 상수로 추출하면 유지보수성이 향상됩니다.const MemoryAddButton = () => { const navigate = useNavigate(); const { roomId } = useParams<{ roomId: string }>(); + const currentRoomId = roomId || '1'; const { openConfirm, closePopup, openSnackbar } = usePopupActions(); // ... const handleRecordWrite = () => { setIsOpen(false); - const currentRoomId = roomId || '1'; navigate(`/memory/record/write/${currentRoomId}`); }; const handlePollCreate = () => { setIsOpen(false); - const currentRoomId = roomId || '1'; navigate(`/memory/poll/write/${currentRoomId}`); }; const handleAIWrite = async () => { setIsOpen(false); - const currentRoomId = roomId || '1'; // ... };Also applies to: 49-49, 56-56
src/types/record.ts (1)
27-44: VoteItemResult 중복 정의 및 VoteRequest.type(boolean) 의미 모호성 리팩터 제안
VoteItemResult의 필드 구성이src/types/memory.ts의VoteItem과 완전히 동일해서, 파일만 다를 뿐 사실상 같은 타입이 이중 정의되어 있습니다. 장기적으로는 하나의 공용 타입으로 통합하고 필요한 곳에서만 import 하는 구조로 리팩터링하면 유지보수성이 좋아질 것 같습니다.VoteRequest의type: boolean은 (이 PR에서 주석이 제거된 상태라면)true/false가 어떤 의미인지 한눈에 파악하기 어렵습니다. 후속 리팩터링에서
type: 'UP' | 'DOWN'같은 string literal union이나 enum으로 의미를 드러내는 쪽을 고려해 보시면 좋겠습니다.현재 PR 범위를 넘는 변경이니 참고용 제안으로만 받아주시면 될 것 같습니다.
src/components/recordwrite/RecordContentSection.tsx (1)
36-45: 중복된 adjustHeight() 호출 제거 고려
autoFocus가 변경될 때마다adjustHeight()가 호출되는데, 이는 32-34번 줄에서 이미content변경 시 호출되고 있어 중복입니다. 이 useEffect의 주 목적은 포커스 처리이므로 37번 줄의adjustHeight()호출은 불필요해 보입니다.다음과 같이 수정할 수 있습니다:
useEffect(() => { - adjustHeight(); - if (autoFocus && textareaRef.current) { const textarea = textareaRef.current; textarea.focus(); const length = textarea.value.length; textarea.setSelectionRange(length, length); } }, [autoFocus]);src/pages/pollwrite/PollWrite.tsx (1)
254-271: 오류 메시지 구체성 고려HTTP 400 오류 시 일반적인 메시지("입력값을 확인해 주세요.")로 변경되었습니다. 이전에는 수정/생성 모드에 따라 다른 메시지를 제공했을 가능성이 있습니다. 사용자 경험 측면에서 더 구체적인 메시지(예: "투표 수정 중 입력값 오류", "투표 생성 중 입력값 오류")가 도움이 될 수 있습니다.
src/components/today-words/MessageList/MessageList.tsx (2)
52-58: 타임스탬프 포맷팅 로직에 간단한 설명 추가를 고려해보세요.날짜 포맷팅 로직이 다소 복잡합니다. 정규식을 사용한 replace 체이닝의 목적(공백 제거 및 마지막 점 제거)을 한 줄로 설명하면 향후 유지보수에 도움이 될 수 있습니다. 하지만 코드 자체로도 충분히 이해 가능하므로 필수는 아닙니다.
70-84: 날짜별 그룹핑 로직에 대한 간단한 설명을 고려해보세요.메시지를 날짜별로 그룹화하고 정렬하는 로직입니다. 코드는 정확하지만, 타입 정의만으로는 "날짜별 구분선 렌더링을 위한 그룹핑"이라는 의도가 명확하지 않을 수 있습니다. 한 줄 주석 추가를 고려해보세요. 단, 코드 가독성이 이미 양호하므로 선택 사항입니다.
src/components/memory/RecordFilters/RecordFilters.tsx (1)
61-106: 페이지 범위 유효성 검증 로직이 입력 플로우와 잘 정합됨
handleConfirm에서의 검증 조건(양수 & 시작 ≤ 끝)과isValid계산식이 동일하게 맞춰져 있어, 버튼 활성화 조건과 실제 확정 조건이 어긋나지 않는 점이 좋습니다.parseInt호출이 여러 번 등장하긴 하지만 현재 규모에서는 크게 문제될 부분은 아니며, 필요 시 나중에 시작/끝 값을 한 번만 계산해 재사용하는 방식으로 리팩터링해도 될 것 같습니다.src/components/common/BookSearchBottomSheet/BookList.tsx (1)
29-59: BookList의 IntersectionObserver는 정상 동작하나, isSearchMode 사용 여부를 한 번 정리해도 좋겠습니다.
lastBookElementRef콜백의 의존성 배열에는isSearchMode가 들어가 있지만, 콜백 본문에서는isSearchMode를 읽지 않고 있습니다.
- 만약 검색 모드일 때 무한 스크롤을 막으려는 의도라면,
entries[0].isIntersecting && hasNextPage && onLoadMore && !isSearchMode처럼 조건에 명시적으로 포함하는 쪽이 의도가 더 분명할 것 같습니다.- 반대로 실제로는 상위에서
hasNextPage/onLoadMore로 이미 제어하고 있어 의미가 없다면, 의존성 배열에서isSearchMode를 제거해 약간이라도 불필요한 재생성을 줄이는 것도 고려해 볼 수 있습니다.src/pages/recordwrite/RecordWrite.tsx (1)
32-121: RecordWrite의 초기화/저장 로직은 안정적으로 보이며, isLoading·에러 처리 부분은 소폭 정리 여지가 있습니다.
initializeData안에서 수정 모드인 경우setIsLoading(false);호출 후return하고,finally에서도 다시setIsLoading(false);를 호출합니다. 동작에는 영향이 없지만 수정 모드 경로에서 동일한 state 업데이트가 두 번 발생하므로,try블록 내부의setIsLoading(false);는 제거해도 될 것 같습니다.- 책 정보 로딩 실패 시와 기록 생성/수정 실패 시 모두,
axiosError.response.data.message와code(400/403/404)에 따라 사용자 메시지를 만드는 로직이 거의 동일합니다. 이 부분을 작은 유틸 함수나 헬퍼로 뽑아 두면, 에러 코드가 늘어나거나 메시지를 수정할 때 한 곳만 손보면 되어 유지보수성이 좋아질 것 같습니다.
PageRangeSection에 넘기는readingProgress,isDisabled,hideToggle값 자체는 명확해서, 이 부분의 주석 제거는 동작이나 이해에 문제 없어 보입니다.Also applies to: 138-255, 294-305
src/pages/today-words/TodayWords.tsx (3)
35-57: 오늘 날짜 문자열 비교 방식이 포맷/스펙 변경에 취약합니다
getTodayDateStrings에서 여러 문자열 포맷을 만들고,getTodayMyMessageCount에서message.timestamp와 단순 문자열 비교로 오늘 여부를 판별하고 있습니다. 서버에서 내려오는item.date포맷이나 타임존 스펙이 바뀌면(예: ISO 문자열, 다른 구분자 등) 오늘 카운트가 어긋날 수 있습니다.API 스펙이 충분히 고정이라면 문제는 없지만,
- 가능한 한 서버에서 내려주는 하나의 표준 포맷만 사용하거나,
timestamp를Date로 파싱해서 연/월/일 기준으로 비교하는 등조금 더 견고한 방식으로 개선하는 것도 한 번 고려해 보셔도 좋겠습니다.
65-79:createdAt를 클라이언트 현재 시각으로 두는 것이 의도인지 확인이 필요합니다
convertToMessage에서createdAt을 항상new Date()로 세팅하고 있어, 서버에서의 실제 작성 시각과는 무관하게 “가져온 시점” 기준 시간이 들어가게 됩니다.이 필드가:
- 단순히 리스트 내 임시 정렬/키 용도라면 큰 문제는 없지만,
- 메시지 정렬, 이후 통계/필터 등 시간 기반 로직에 활용된다면 재진입 시마다 값이 달라져 정렬이 흔들릴 수 있습니다.
혹시 서버에서 실제 생성 시각(예: ISO datetime)을 제공하고 있다면, 그 값을 기준으로
createdAt을 세팅하는 편이 더 안전한지 한 번 점검을 부탁드립니다.
318-325: 초기 로딩 스피너 레이아웃 인라인 스타일 적용은 적절해 보입니다초기 로딩 시 중앙 정렬을 위해 wrapper
div에 플렉스, 높이 200px을 주는 변경은 시각적으로 더 명확한 로딩 상태를 제공하면서도 부수 영향이 거의 없어 보입니다. 추후 반복 사용 시에는 styled 컴포넌트로의 추출 정도만 고려하시면 될 것 같습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (81)
src/api/feeds/createFeed.ts(1 hunks)src/api/feeds/getWriteInfo.ts(0 hunks)src/api/feeds/updateFeed.ts(0 hunks)src/api/images/uploadImage.ts(1 hunks)src/api/memory/getMemoryPosts.ts(1 hunks)src/api/record/createAiReview.ts(0 hunks)src/api/record/createRecord.ts(0 hunks)src/api/record/createVote.ts(0 hunks)src/api/record/deleteRecord.ts(1 hunks)src/api/record/deleteVote.ts(1 hunks)src/api/record/getAiUsage.ts(1 hunks)src/api/record/pinRecordToFeed.ts(1 hunks)src/api/record/postVote.ts(1 hunks)src/api/record/updateRecord.ts(0 hunks)src/api/record/updateVote.ts(0 hunks)src/api/roomPosts/postRoomPostLike.ts(0 hunks)src/api/rooms/createDailyGreeting.ts(1 hunks)src/api/rooms/createRoom.ts(0 hunks)src/api/rooms/deleteDailyGreeting.ts(1 hunks)src/api/rooms/getBookPage.ts(1 hunks)src/api/rooms/getDailyGreeting.ts(1 hunks)src/api/rooms/getRoomMembers.ts(1 hunks)src/api/rooms/getRoomPlaying.ts(0 hunks)src/api/rooms/leaveRoom.ts(1 hunks)src/components/common/BookSearchBottomSheet/BookList.tsx(4 hunks)src/components/common/BookSearchBottomSheet/BookSearchBottomSheet.styled.ts(0 hunks)src/components/common/BookSearchBottomSheet/BookSearchBottomSheet.tsx(3 hunks)src/components/common/BookSearchBottomSheet/useBookSearch.ts(5 hunks)src/components/common/CommentBottomSheet/GlobalCommentBottomSheet.tsx(4 hunks)src/components/common/Post/Reply.tsx(0 hunks)src/components/common/Post/SubReply.tsx(0 hunks)src/components/creategroup/ActivityPeriodSection/ActivityPeriodSection.tsx(1 hunks)src/components/creategroup/ActivityPeriodSection/DateWheel.tsx(0 hunks)src/components/creategroup/MemberLimitSection.tsx(0 hunks)src/components/creategroup/PrivacySettingSection/PasswordInputSection.styled.ts(0 hunks)src/components/creategroup/PrivacySettingSection/PasswordInputSection.tsx(1 hunks)src/components/createpost/PhotoSection.tsx(0 hunks)src/components/createpost/PostContentSection.styled.ts(0 hunks)src/components/createpost/TagSelectionSection.tsx(0 hunks)src/components/group/GroupActionBottomSheet.tsx(1 hunks)src/components/group/HotTopicSection.tsx(1 hunks)src/components/members/MemberList.tsx(0 hunks)src/components/memory/MemoryAddButton/MemoryAddButton.tsx(1 hunks)src/components/memory/MemoryContent/MemoryContent.tsx(0 hunks)src/components/memory/RecordFilters/PageInputMode.styled.ts(0 hunks)src/components/memory/RecordFilters/RecordFilters.tsx(1 hunks)src/components/memory/RecordItem/PollRecord.tsx(5 hunks)src/components/memory/RecordItem/RecordItem.tsx(8 hunks)src/components/memory/SortDropdown.tsx(0 hunks)src/components/memory/UploadProgressBar/UploadProgressBar.tsx(2 hunks)src/components/pollwrite/PollCreationSection.tsx(1 hunks)src/components/recordwrite/PageRangeSection.styled.ts(0 hunks)src/components/recordwrite/PageRangeSection.tsx(1 hunks)src/components/recordwrite/RecordContentSection.tsx(2 hunks)src/components/today-words/MessageInput.styled.ts(0 hunks)src/components/today-words/MessageInput.tsx(1 hunks)src/components/today-words/MessageList/MessageActionBottomSheet.tsx(0 hunks)src/components/today-words/MessageList/MessageList.tsx(2 hunks)src/hooks/useCreateFeed.ts(2 hunks)src/hooks/useUpdateFeed.ts(0 hunks)src/mocks/members.mock.ts(1 hunks)src/pages/group/CreateGroup.tsx(3 hunks)src/pages/groupDetail/ParticipatedGroupDetail.tsx(3 hunks)src/pages/groupMembers/GroupMembers.tsx(1 hunks)src/pages/memory/Memory.styled.ts(1 hunks)src/pages/memory/Memory.tsx(1 hunks)src/pages/pollwrite/PollWrite.tsx(7 hunks)src/pages/post/CreatePost.tsx(3 hunks)src/pages/post/UpdatePost.tsx(1 hunks)src/pages/recordwrite/RecordWrite.tsx(1 hunks)src/pages/today-words/TodayWords.tsx(4 hunks)src/stores/useCommentBottomSheetStore.ts(1 hunks)src/stores/usePopupStore.ts(0 hunks)src/styles/global/global.ts(0 hunks)src/types/memory.ts(2 hunks)src/types/record.ts(1 hunks)src/types/room.ts(1 hunks)src/types/roomPostLike.ts(1 hunks)src/types/today.ts(0 hunks)src/utils/isbn.ts(1 hunks)src/utils/roomStatus.ts(0 hunks)
💤 Files with no reviewable changes (31)
- src/components/creategroup/ActivityPeriodSection/DateWheel.tsx
- src/types/today.ts
- src/components/common/Post/Reply.tsx
- src/components/memory/SortDropdown.tsx
- src/api/roomPosts/postRoomPostLike.ts
- src/components/creategroup/MemberLimitSection.tsx
- src/api/record/updateRecord.ts
- src/components/common/BookSearchBottomSheet/BookSearchBottomSheet.styled.ts
- src/hooks/useUpdateFeed.ts
- src/components/today-words/MessageInput.styled.ts
- src/api/record/createRecord.ts
- src/components/createpost/PostContentSection.styled.ts
- src/components/creategroup/PrivacySettingSection/PasswordInputSection.styled.ts
- src/components/recordwrite/PageRangeSection.styled.ts
- src/api/rooms/getRoomPlaying.ts
- src/utils/roomStatus.ts
- src/components/members/MemberList.tsx
- src/components/memory/MemoryContent/MemoryContent.tsx
- src/api/record/updateVote.ts
- src/components/common/Post/SubReply.tsx
- src/styles/global/global.ts
- src/components/today-words/MessageList/MessageActionBottomSheet.tsx
- src/api/feeds/getWriteInfo.ts
- src/api/rooms/createRoom.ts
- src/stores/usePopupStore.ts
- src/components/createpost/PhotoSection.tsx
- src/api/record/createAiReview.ts
- src/components/memory/RecordFilters/PageInputMode.styled.ts
- src/api/feeds/updateFeed.ts
- src/api/record/createVote.ts
- src/components/createpost/TagSelectionSection.tsx
🧰 Additional context used
🧬 Code graph analysis (14)
src/pages/pollwrite/PollWrite.tsx (2)
src/api/rooms/getBookPage.ts (1)
getBookPage(17-25)src/api/record/createVote.ts (1)
createVote(6-9)
src/components/memory/RecordItem/PollRecord.tsx (2)
src/types/memory.ts (1)
PollOption(75-83)src/components/memory/RecordItem/PollRecord.styled.ts (1)
PollOption(23-32)
src/api/rooms/getDailyGreeting.ts (1)
src/api/index.ts (1)
apiClient(7-13)
src/components/common/BookSearchBottomSheet/BookList.tsx (1)
src/components/common/BookSearchBottomSheet/BookSearchBottomSheet.styled.ts (2)
BookList(159-163)BookItem(165-177)
src/api/record/getAiUsage.ts (1)
src/api/index.ts (1)
apiClient(7-13)
src/components/common/BookSearchBottomSheet/useBookSearch.ts (1)
src/api/books/getSearchBooks.ts (2)
getSearchBooks(41-59)convertToSearchedBooks(61-70)
src/types/record.ts (1)
src/types/memory.ts (1)
VoteItem(1-7)
src/pages/post/CreatePost.tsx (2)
src/utils/isbn.ts (1)
ensureIsbn13(16-21)src/hooks/useCreateFeed.ts (1)
useCreateFeed(11-157)
src/api/record/postVote.ts (1)
src/api/index.ts (1)
apiClient(7-13)
src/pages/today-words/TodayWords.tsx (2)
src/types/today.ts (2)
TodayCommentItem(12-21)Message(1-10)src/api/rooms/getDailyGreeting.ts (1)
getDailyGreeting(20-34)
src/components/common/CommentBottomSheet/GlobalCommentBottomSheet.tsx (2)
src/api/comments/postReply.ts (1)
postReply(19-31)src/components/common/CommentBottomSheet/GlobalCommentBottomSheet.styled.ts (2)
Content(49-53)LoadingState(55-63)
src/pages/group/CreateGroup.tsx (1)
src/types/room.ts (1)
CreateRoomRequest(20-30)
src/types/memory.ts (1)
src/types/record.ts (1)
VoteItem(11-13)
src/components/today-words/MessageList/MessageList.tsx (1)
src/api/rooms/deleteDailyGreeting.ts (1)
deleteDailyGreeting(12-26)
🔇 Additional comments (65)
src/mocks/members.mock.ts (1)
11-18: 주석 제거 확인됨 — mock 데이터에서는 안전합니다.이 변경은 Member 객체의 profileImage 필드에서 인라인 주석을 제거하는 것으로, 런타임 동작이나 데이터 구조에는 영향을 주지 않습니다. Mock 데이터 파일에서의 주석 제거는 PR 목표와 일치하며, TypeScript 인터페이스(1-8줄)가 충분한 타입 정보를 제공하므로 안전한 변경입니다.
다만, 제거된 주석이 테스트/개발 과정에서 중요한 정보를 담고 있지 않았는지 확인해 주시기 바랍니다.
src/components/today-words/MessageInput.tsx (1)
89-89: 주석 제거 후 코드 가독성이 잘 유지되고 있습니다.
MessageInputProps인터페이스가 이미 명확한 타입 정보를 제공하고 있으며, 남은 코드 로직이 직관적이어서 제거된 주석이 없어도 의도가 명확합니다. 특히:
- Line 89:
disabled상태에 따른 placeholder 전환 로직이 자체 설명적(self-documenting)입니다.- Line 93: 조합 입력(composition) 상태 관리가 변수명과 핸들러 네이밍으로 충분히 이해됩니다.
TypeScript 타입 시스템이 주석을 효과적으로 대체하고 있어, PR의 목표(주석 정리를 통한 가독성 및 유지보수성 개선)가 이 컴포넌트에서는 잘 달성되었습니다.
Also applies to: 93-93
src/pages/groupMembers/GroupMembers.tsx (1)
59-70: 멤버 네비게이션 로직 승인
handleMemberClick의 자기 자신 여부 확인 후 다른 라우트로 분기하는 로직이 명확하고 효율적입니다. 이 구현 방식은 좋습니다.src/stores/useCommentBottomSheetStore.ts (1)
14-25: LGTM! 포맷팅 개선이 가독성을 향상시킵니다.제네릭 타입 선언 및 함수 구현의 포맷팅 변경이 코드의 가독성을 개선하며, 런타임 동작이나 타입 안전성에는 영향을 주지 않습니다. PR의 목표인 코드 정리 및 가독성 향상에 잘 부합합니다.
src/components/memory/MemoryAddButton/MemoryAddButton.tsx (2)
13-13: LGTM!주석 제거 작업이 정상적으로 반영되었습니다.
useParams타입 정의가 명확하게 유지되어 있습니다.
54-111: LGTM!
handleAIWrite함수의 구현이 잘 되어 있습니다:
- API 호출에 대한 try-catch 에러 처리가 적절합니다
- 기록 개수 및 이용 횟수 검증 로직이 명확합니다
- 사용자에게 적절한 피드백(snackbar, confirm modal)을 제공합니다
src/components/creategroup/ActivityPeriodSection/ActivityPeriodSection.tsx (2)
41-44: 변경 사항 확인 필요AI 요약에서는 "실행 로직이 수정되지 않았다"고 명시되어 있으나, 43번 라인이 변경된 것으로 표시되어 있습니다. 이것이 단순히 주변 주석 제거로 인한 것인지, 아니면 실제 로직 변경이 있었는지 확인해 주세요.
현재 날짜 차이 계산 로직 자체는 정확합니다 (시작일과 종료일 모두 포함하는 inclusive counting).
1-248: 주석 제거 완료, LGTM!전체적으로 코드 구조와 로직이 잘 유지되어 있습니다. 날짜 유효성 검사, 범위 계산, 핸들러 로직 모두 정상적으로 동작할 것으로 보입니다.
src/components/common/CommentBottomSheet/GlobalCommentBottomSheet.tsx (3)
32-32: 코드 정리 변경사항이 적절합니다.빈 줄 추가 및 trailing comma 적용은 코드 가독성과 Git diff 품질을 개선하는 모범 사례입니다. 기능적 변경 없이 코드 스타일만 개선되었습니다.
Also applies to: 66-66, 73-73, 146-146
151-151: 컴포넌트 포맷팅 변경이 적절합니다.ReplyList 컴포넌트를 단일 라인으로 변경한 것은 props 수가 적어 가독성을 해치지 않으며, 코드를 더 간결하게 만듭니다.
176-176: 주석 제거가 적절합니다.export 문은 자명하므로 주석 제거가 PR 목표와 일치하며 코드 가독성에 부정적 영향을 미치지 않습니다.
src/components/group/GroupActionBottomSheet.tsx (2)
10-17: 주석 제거가 적절합니다.인터페이스 정의가 명확하고 자기 문서화되어 있습니다. 각 prop의 이름과 타입이 충분히 설명적이므로, 제거된 주석은 중복되었을 가능성이 높습니다. TypeScript의 타입 시스템이 충분한 문서화를 제공하고 있습니다.
19-66: PR 목표에 부합하는 리팩터링입니다.컴포넌트 구현이 간단명료하며, 핸들러 함수들과 조건부 렌더링 로직이 충분히 자기 설명적입니다. 제거된 주석들은 코드의 명확성에 중요하지 않았던 것으로 판단됩니다. 코드 가독성과 유지보수성에 부정적인 영향은 없습니다.
src/api/record/postVote.ts (1)
8-11: 코드 가독성 개선이 잘 되었습니다!POST 요청을 여러 줄로 분리한 포맷팅 변경이 코드의 가독성을 향상시켰습니다. 기능적 로직은 그대로 유지되며, TypeScript 타입도 적절하게 지정되어 있습니다.
src/types/record.ts (3)
1-25: 레코드/투표 생성 관련 타입은 시그니처 변경 없이 정리만 된 것으로 보입니다
CreateRecordRequest,CreateRecordData,VoteItem,CreateVoteRequest,CreateVoteData모두 필드 이름과 타입이 자연스럽고, ai_summary 기준으로도 구조 변경은 없는 것으로 보여 주석 제거로 인한 API 스펙 변경 위험은 없어 보입니다. 이 구간은 그대로 머지해도 무방해 보입니다.
46-60: 레코드/투표 수정 관련 타입도 주석 제거 외에 의미 변화는 없어 보입니다
UpdateRecordRequest/UpdateRecordData/UpdateVoteRequest/UpdateVoteData는 필드 수도 적고 역할도 명확해서, 주석이 제거되어도 혼동 가능성이 거의 없어 보입니다. 타입 시그니처 역시 유지되고 있어 이 부분도 그대로 진행해도 괜찮겠습니다.
62-76: AI 사용량/응답 래퍼 타입 정의도 문제 없으며, 주석 제거 영향 미미해 보입니다
CreateAiReviewData,AiUsageData,ApiResponse<T>모두 필드명이 역할을 충분히 설명하고 있어, 별도의 주석 없이도 의미 파악이 가능합니다. 기존 공개 타입 시그니처를 그대로 유지하면서 불필요한 주석만 제거한 것으로 보여, 이 구간 역시 안전하게 머지 가능해 보입니다.src/components/recordwrite/RecordContentSection.tsx (1)
13-13: 좋은 변경입니다!선택적 prop에 기본값을 제공하는 것은 좋은 패턴입니다.
Also applies to: 19-19
src/pages/pollwrite/PollWrite.tsx (2)
228-249: LGTM!투표 생성 로직이 정확하며, 포맷팅 변경만 있습니다.
293-293: LGTM!타이틀 문자열의 따옴표 스타일 변경은 문제없습니다.
Also applies to: 317-317
src/components/pollwrite/PollCreationSection.tsx (1)
20-21: 좋은 변경입니다!선택적 prop들에 기본값을 제공하는 것은 좋은 패턴이며, RecordContentSection과 일관성 있게 적용되었습니다.
Also applies to: 29-30
src/components/today-words/MessageList/MessageList.tsx (2)
41-43: 상태 동기화 로직이 올바르게 구현되었습니다.
initialMessagesprop이 변경될 때마다 로컬 상태를 동기화하는 패턴이 정확합니다.
94-140: 삭제 플로우가 잘 구현되었습니다.API 호출 후 로컬 상태 업데이트(Line 102), 콜백 실행, 그리고 사용자 피드백(스낵바)이 올바른 순서로 처리되고 있습니다. 에러 처리도 포괄적으로 구현되어 있습니다(API 실패,
isSuccess: false응답, roomId 누락 케이스 모두 처리).src/api/rooms/deleteDailyGreeting.ts (1)
12-26: LGTM!주석 제거 후에도 함수 로직과 에러 처리가 올바르게 유지되어 있습니다.
src/components/recordwrite/PageRangeSection.tsx (2)
29-40: LGTM!인터페이스 정의가 깔끔하게 유지되어 있으며, 주석 제거 후에도 props 타입이 명확합니다.
42-95: LGTM!입력 검증, 토글 핸들링, 툴팁 상태 관리 로직이 모두 올바르게 유지되어 있습니다.
src/utils/isbn.ts (2)
1-4: LGTM!
normalizeIsbn의toUpperCase()추가가 적절합니다. ISBN-10의 체크 디지트로 'X'가 사용될 수 있으며,isIsbn10정규식이 대문자 'X'를 기대하므로 정규화 단계에서 대문자 변환이 필요합니다.
6-14: LGTM!ISBN-10에서 ISBN-13으로의 변환 로직이 EAN-13 체크섬 표준을 올바르게 구현하고 있습니다.
src/types/memory.ts (3)
56-73: LGTM!
Record인터페이스에profileImageUrl,isLiked,isLocked필드가 추가되어Post인터페이스와 일관성이 유지됩니다.
75-83: LGTM!
PollOption인터페이스에voteItemId와isVoted필드가 추가되어 투표 기능 구현에 필요한 정보를 제공합니다.
1-7: VoteItem 타입 중복 확인됨 - 정리 필요현재 코드베이스에
VoteItem인터페이스가 3개 파일에 서로 다른 형태로 정의되어 있습니다:
src/types/memory.ts:{voteItemId, itemName, percentage, count, isVoted}src/types/record.ts:{itemName}(별도로VoteItemResult타입 정의됨)src/api/rooms/getRoomPlaying.ts:{itemName}이는 의도적인 분리(요청/응답, 메모리 vs 레코드 컨텍스트)이거나 타입 중복일 가능성이 있습니다. 다음 중 하나를 검토하세요:
- 타입 통합: 공통 기본 타입을 정의하고 필요시 확장
- 명확한 네이밍: 목적별로 명확한 이름(예:
MemoryVoteItem,RecordVoteItem)으로 구분- 타입 분리 의도 문서화: 각각 다른 용도임을 명시
src/components/memory/UploadProgressBar/UploadProgressBar.tsx (1)
21-33: LGTM!프로그레스 바 로직이 명확하게 구현되어 있습니다. 주석 제거 후에도 코드 흐름이 이해하기 쉽습니다.
src/types/roomPostLike.ts (1)
1-16: RoomPostLike 관련 타입 정의 유지 상태 양호요청/응답 인터페이스 구조나 필드 타입이 모두 일관적이고, 주석 제거로 인한 의미 손실도 없어 보입니다. 현재 이름만으로도 역할이 잘 드러나서 타입 정의 그대로 사용해도 무방해 보입니다.
src/api/rooms/leaveRoom.ts (1)
3-19: leaveRoom API 동작 변화 없음, 주석 제거만 진행된 것으로 보임응답 타입과 삭제 요청 경로, 에러 처리 흐름 모두 기존 패턴과 일관적이며, 주석 제거로 인한 이해도 저하는 없어 보입니다. 이 상태로 유지해도 괜찮겠습니다.
src/api/memory/getMemoryPosts.ts (1)
7-31: 기록장 조회 쿼리 파라미터 구성 로직 적절
type기본값,sort의 group 타입 한정,pageStart/pageEnd와 boolean 플래그들의 조건부 append 처리까지 모두 안정적으로 보입니다. 주석 없이도 의도가 코드에 잘 드러나서 유지보수에 문제 없을 것 같습니다.src/api/rooms/createDailyGreeting.ts (1)
3-16: 오늘의 한마디 생성 타입/요청 구조 명확요청 바디(
content)와 응답 래퍼(CreateDailyGreetingResponse) 구조가 단순·일관적이며, 주석 제거로 인해 추가 설명이 꼭 필요해 보이지도 않습니다. 현재 형태로 충분히 자기 설명적인 코드라고 판단됩니다.src/api/record/deleteRecord.ts (1)
4-22: 기록 삭제 API 타입/에러 처리 모두 기존 패턴과 일관
DeleteRecordData에roomId만 담고, 공통ApiResponse<DeleteRecordData>를 사용하는 구조가 다른 API들과 잘 맞습니다. try/catch에서 로그 후 재throw 패턴도 동일해 동작 상 문제는 없어 보입니다.src/components/common/BookSearchBottomSheet/useBookSearch.ts (1)
3-7: 책 검색·페이지네이션 로직이 정리되어 있고 동작도 일관
convertToSearchedBooks를 사용해 API 결과를 통일된SearchedBook→Book형태로 변환하고, 추가 페이지 시startIndex로 ID를 이어붙이는 방식이 깔끔합니다.performSearch에서 새 검색과 “더 보기”를isNewSearch로 분리하고,isLoadingMore·hasNextPage가드로 중복 호출을 막는 흐름도 안정적으로 보입니다.loadMoreSearchResults와 debounce 기반handleSearchQueryChange조합 역시 현재 요구사항 범위에서는 충분히 안전해 보여, 주석 제거로 인한 이해도 저하는 없다고 판단됩니다.Also applies to: 119-178, 195-213, 246-253
src/components/memory/RecordItem/PollRecord.tsx (1)
19-25: 투표 토글 및 상위 동기화 로직이 명확하게 정리됨
postId,shouldBlur,onVoteUpdate추가로 컴포넌트 책임이 더 분명해졌고,shouldBlur일 때 클릭을 막는 처리(가드 + 스타일)도 일관적입니다.e.stopPropagation()으로 투표 클릭 시 상위 카드 클릭 이벤트를 차단한 부분이 UX 측면에서 특히 좋아 보입니다.- 응답의
voteItems를 기반으로percentage,count,isVoted,isHighest를 다시 계산해currentOptions와 상위 콜백 둘 다 갱신하는 흐름이 자연스럽고, 에러 코드별 메시지 분기와 마지막에response.message를 사용하는 fallback도 적절합니다.- 이 정도 복잡도의 로직은 현재 코드만으로도 충분히 읽히기 때문에, 제거된 주석이 없더라도 이해나 유지보수에 큰 문제는 없을 것으로 보입니다.
Also applies to: 41-68, 74-147, 149-179
src/components/group/HotTopicSection.tsx (1)
39-125: HotTopicSection 드래그/스와이프 로직은 주석 제거 후에도 일관적으로 보입니다.드래그 상태 관리, translateX 계산, 인덱스 기반 스냅 로직 흐름이 명확하고,
containerWidth = 100으로 슬라이드 1칸을 100% 단위로 두는 설계도 기존 퍼센트 기반 이동과 자연스럽게 맞습니다. 비교적 복잡한 부분이지만 함수·변수 이름만으로 의도가 잘 드러나서, 이 파일에서 제거된 주석이 없더라도 유지보수에는 무리가 없어 보입니다.src/api/record/getAiUsage.ts (1)
4-8: getAiUsage API는 주석/포맷 정리만 있고 동작 변화는 없어 보입니다.
apiClient.get<GetAiUsageResponse>(\/rooms/${roomId}/users/ai-usage`)호출 후response.data`만 반환하는 패턴이 다른 API들과 일관적이고, 한 줄 템플릿 리터럴 정리도 가독성 측면에서 무리 없습니다.src/api/record/deleteVote.ts (1)
4-18: deleteVote API는 주석 제거 외에는 기존 동작을 잘 유지하고 있습니다.
DeleteVoteData와DeleteVoteResponse가 공통ApiResponse패턴과 맞고,try/catch에서 에러를 로그만 남기고 다시 던지는 구조도 호출부 공통 처리에 적합합니다. roomId 용도와 엔드포인트 경로가 코드만으로 충분히 드러나서, 이 부분의 주석 제거는 크게 문제 없다고 판단됩니다.src/api/rooms/getRoomMembers.ts (1)
48-60: getRoomMembers의 에러 코드(140011) 처리 로직은 주석 없이도 충분히 명확합니다.
AxiosError이면서error.response?.data?.code === 140011일 때만'방 접근 권한이 없습니다.'라는 새Error를 던지고, 나머지는 원본 에러를 그대로 재던져 호출부에서 구분할 수 있게 되어 있습니다. 도메인 코드와 메시지가 구체적이라, 여기에 붙어 있던 설명 주석은 없어도 유지보수에 큰 영향은 없을 것 같습니다.src/types/room.ts (1)
49-62: Room 관련 타입 확장은 자연스럽지만, RoomMember.joinedAt 필수 여부는 한 번 확인해 주세요.
RoomMember에profileImageUrl?: string과joinedAt: string이 추가되고,ApiResponse<T>에message와data가 포함된 구조는 현재 여러 API 응답에서 사용하는 패턴과 잘 맞는 것 같습니다.다만
joinedAt을 필수(string)로 두면, 일부 오래된 방이나 테스트 환경에서 해당 필드가 누락된 응답이 들어올 경우 런타임에서undefined를 문자열로 가정하게 될 수 있습니다.
- 백엔드 스키마상 항상 내려오는 필드라면 지금처럼 필수로 두는 것이 맞고,
- 선택적으로만 내려온다면
joinedAt?: string으로 조정하는 편이 안전합니다.한 번 실제 응답 스키마와 맞는지만 확인해 주시면 좋겠습니다.
src/api/images/uploadImage.ts (1)
7-12: UploadImageResponse의 data를 optional로 둔 변경이 다중 업로드 로직과 잘 맞습니다.
UploadImageResponse의data?: UploadImageData선언과,uploadMultipleImages에서value.isSuccess && value.data?.imageUrl로 방어적으로 접근하는 코드가 서로 일관적입니다. 일부 실패/에러 응답에서data가 없을 수 있는 상황까지 타입으로 표현한 것이어서, 주석 제거와 함께 타입 정의도 실제 사용 패턴에 더 잘 맞게 정리된 것으로 보입니다.Also applies to: 43-85
src/api/record/pinRecordToFeed.ts (1)
15-15: 변경사항 확인 완료주석 제거 및 trailing comma 추가로 코드 일관성이 개선되었습니다. 기능적 변경사항이 없어 안전합니다.
src/api/rooms/getDailyGreeting.ts (1)
20-34: 포맷팅 개선 확인파라미터 destructuring을 multiline 형식으로 변경하여 가독성이 개선되었습니다. 기능적 변경사항이 없습니다.
src/components/common/BookSearchBottomSheet/BookSearchBottomSheet.tsx (2)
54-64: body scroll 관리 개선useEffect cleanup이 제대로 추가되어 컴포넌트 언마운트 시 body overflow 속성이 올바르게 복원됩니다. 이는 메모리 누수를 방지하고 사용자 경험을 개선합니다.
129-136: BookList 컴포넌트 props 전달 개선
isLoadingMore와isSearchModeprops가 추가되어 로딩 상태 및 검색 모드를 BookList에서 활용할 수 있게 되었습니다. UI 일관성 향상에 기여합니다.src/api/feeds/createFeed.ts (1)
28-31: 함수 시그니처 간소화함수 시그니처를 single-line으로 변경하여 코드가 더 간결해졌습니다. 기능적 변경사항은 없습니다.
src/hooks/useCreateFeed.ts (1)
15-154: Snackbar UI 일관성 개선모든
openSnackbar호출에variant: 'top'과onClose: closePopup을 추가하여 사용자 경험의 일관성이 향상되었습니다. 검증 로직은 그대로 유지되어 안전합니다.src/components/memory/RecordItem/RecordItem.tsx (3)
251-295: 클릭 핸들러 로직 통합
handleClick함수가 통합되어 코드 중복이 제거되고 가독성이 개선되었습니다.shouldBlur상태에서의 early return으로 조건부 로직이 명확해졌습니다.
335-342: PollRecord의 onVoteUpdate 비활성화
onVoteUpdate가 빈 함수로 변경되어 투표 업데이트 시 외부 상태 변경이 발생하지 않습니다. 이것이 의도된 동작인지 확인이 필요합니다. 투표 후 UI 업데이트가 제대로 작동하는지 테스트하세요.
105-122: Poll 옵션 파라미터 파싱은 올바르게 구현되어 있습니다.검증 결과, RecordItem.tsx에서 전송한
options파라미터는 PollWrite.tsx에서 다음과 같이 올바르게 처리되고 있습니다:
- 전송: RecordItem.tsx 110줄에서
JSON.stringify(pollOptions?.map(option => option.text) || [])로 직렬화- 수신: PollWrite.tsx 52줄에서
searchParams.get('options')로 파라미터 추출- 파싱: PollWrite.tsx 68줄에서
JSON.parse(decodeURIComponent(existingOptions))로 역직렬화- 에러 처리: 70-71줄에서 파싱 실패 시 예외 처리 및 로깅 (console.error)
URLSearchParams는 자동으로 값을 인코딩/디코딩하므로 양방향 변환이 정상적으로 작동합니다.
src/pages/memory/Memory.tsx (3)
93-148: 데이터 로딩 로직 유지
loadMemoryPosts함수의 핵심 로직이 유지되어 기능이 보존되었습니다. 에러 처리와 필터링 로직도 그대로 작동합니다.
141-143: 에러 처리 후 early return 추가Line 141-142에서 총평 조회 권한이 없을 때
setActiveFilter(null)을 호출하고 early return을 추가한 것은 올바른 에러 처리입니다. 불필요한 에러 메시지 설정을 방지합니다.
208-232: 메모이제이션된 필터링 로직
useMemo를 사용한currentRecords,sortedRecords,filteredRecords계산 로직이 유지되어 성능이 보장됩니다. 필터링 조건이 명확하게 구현되어 있습니다.src/pages/groupDetail/ParticipatedGroupDetail.tsx (1)
78-157: 주석 제거가 적절합니다.주석 제거가 깔끔하게 이루어졌으며, 모든 기능 로직은 그대로 유지되고 있습니다.
src/pages/post/UpdatePost.tsx (1)
38-116: 코드 정리가 적절합니다.console.log 및 주석 제거로 코드 가독성이 향상되었으며, 핵심 로직은 모두 유지되고 있습니다.
src/pages/group/CreateGroup.tsx (3)
10-10: import 경로 수정이 적절합니다.이중 슬래시를 제거하여 올바른 import 경로로 수정되었습니다.
83-83: 중복 제출 방지 로직이 잘 추가되었습니다.
isSubmitting체크로 중복 제출을 방지하는 가드가 적절하게 추가되었습니다.
187-189: 비밀번호 유효성 검사가 적절합니다.4자리 숫자 비밀번호 검증 로직(
/^\d{4}$/)이 올바르게 구현되어 있습니다.src/pages/post/CreatePost.tsx (2)
24-24:ensureIsbn13사용이 적절합니다.ISBN 유효성 검사 및 정규화 로직을 유틸리티 함수로 중앙화하여 코드 재사용성이 향상되었습니다.
76-79:onSuccess콜백 시그니처 변경이 일관적입니다.
useCreateFeed훅의 변경사항과 일치하도록onSuccess콜백이 매개변수 없이 호출되도록 업데이트되었습니다. 네비게이션 로직은 콜백 내부에서 적절하게 처리되고 있습니다.src/pages/today-words/TodayWords.tsx (1)
193-199: 무한 스크롤 조건 로직은 의도대로 잘 동작해 보입니다
scrollHeight - 100여유를 두고,!isLoadingMore && !isLast && hasInitiallyLoaded를 함께 체크하는 조건은 과도한 호출을 막으면서도 UX 측면에서 자연스러운 트리거 포인트로 보입니다. 별도 이슈는 없어 보입니다.
| const PasswordInputSection = ({ password, onPasswordChange }: PasswordInputSectionProps) => { | ||
| const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
| const value = e.target.value; | ||
| // 숫자만 허용하고 최대 4자리까지만 입력 가능 | ||
| const numericValue = value.replace(/[^0-9]/g, ''); | ||
| if (numericValue.length <= 4) { | ||
| onPasswordChange(numericValue); | ||
| } | ||
| }; | ||
|
|
||
| const handleClose = () => { | ||
| onPasswordChange(''); // 입력된 숫자 전체 삭제 | ||
| onPasswordChange(''); | ||
| }; |
There was a problem hiding this comment.
미사용 prop을 호출해야 합니다.
onClose prop이 정의되어 있지만 handleClose 함수에서 호출되지 않습니다. 부모 컴포넌트에 닫기 이벤트를 알리기 위해 onClose를 호출해야 할 것으로 보입니다.
다음 diff를 적용하여 수정하세요:
-const PasswordInputSection = ({ password, onPasswordChange }: PasswordInputSectionProps) => {
+const PasswordInputSection = ({ password, onPasswordChange, onClose }: PasswordInputSectionProps) => {
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
const numericValue = value.replace(/[^0-9]/g, '');
if (numericValue.length <= 4) {
onPasswordChange(numericValue);
}
};
const handleClose = () => {
onPasswordChange('');
+ onClose();
};📝 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.
| const PasswordInputSection = ({ password, onPasswordChange }: PasswordInputSectionProps) => { | |
| const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |
| const value = e.target.value; | |
| // 숫자만 허용하고 최대 4자리까지만 입력 가능 | |
| const numericValue = value.replace(/[^0-9]/g, ''); | |
| if (numericValue.length <= 4) { | |
| onPasswordChange(numericValue); | |
| } | |
| }; | |
| const handleClose = () => { | |
| onPasswordChange(''); // 입력된 숫자 전체 삭제 | |
| onPasswordChange(''); | |
| }; | |
| const PasswordInputSection = ({ password, onPasswordChange, onClose }: PasswordInputSectionProps) => { | |
| const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |
| const value = e.target.value; | |
| const numericValue = value.replace(/[^0-9]/g, ''); | |
| if (numericValue.length <= 4) { | |
| onPasswordChange(numericValue); | |
| } | |
| }; | |
| const handleClose = () => { | |
| onPasswordChange(''); | |
| onClose(); | |
| }; |
🤖 Prompt for AI Agents
In src/components/creategroup/PrivacySettingSection/PasswordInputSection.tsx
around lines 15 to 26, the component defines a handleClose that clears the
password but does not call the onClose prop; update the component props to
accept onClose (if not already typed) and invoke onClose() inside handleClose
after calling onPasswordChange('') so the parent is notified when the input is
closed/cleared; ensure types are updated (PasswordInputSectionProps) and any
usages are adjusted accordingly.
| const loadMessages = useCallback( | ||
| async (cursor?: string, isRefresh = false) => { | ||
| if (!roomId) return; | ||
|
|
||
| try { | ||
| if (isRefresh) { | ||
| setIsLoading(true); | ||
| } else { | ||
| setIsLoadingMore(true); | ||
| } | ||
| try { | ||
| if (isRefresh) { | ||
| setIsLoading(true); | ||
| } else { | ||
| setIsLoadingMore(true); | ||
| } | ||
|
|
||
| const response = await getDailyGreeting({ | ||
| roomId: parseInt(roomId), | ||
| cursor: cursor || undefined, | ||
| }); | ||
| const response = await getDailyGreeting({ | ||
| roomId: parseInt(roomId), | ||
| cursor: cursor || undefined, | ||
| }); | ||
|
|
||
| if (response.isSuccess) { | ||
| const newMessages = response.data.todayCommentList.map(convertToMessage); | ||
|
|
||
| if (isRefresh) { | ||
| setMessages(newMessages); | ||
| if (response.isSuccess) { | ||
| const newMessages = response.data.todayCommentList.map(convertToMessage); | ||
|
|
||
| if (isRefresh) { | ||
| setMessages(newMessages); | ||
| } else { | ||
| setMessages(prev => [...prev, ...newMessages]); | ||
| } | ||
|
|
||
| setNextCursor(response.data.nextCursor); | ||
| setIsLast(response.data.isLast); | ||
| setHasInitiallyLoaded(true); | ||
|
|
||
| if (isRefresh) { | ||
| setTimeout(() => { | ||
| window.scrollTo({ top: document.body.scrollHeight, behavior: 'auto' }); | ||
| }, 100); | ||
| } | ||
| } else { | ||
| setMessages(prev => [...prev, ...newMessages]); | ||
| openSnackbar({ | ||
| message: response.message || '오늘의 한마디 목록을 불러오는데 실패했습니다.', | ||
| variant: 'top', | ||
| onClose: () => {}, | ||
| }); | ||
| } | ||
|
|
||
| setNextCursor(response.data.nextCursor); | ||
| setIsLast(response.data.isLast); | ||
| setHasInitiallyLoaded(true); | ||
|
|
||
| // 초기 로딩 시 스크롤을 맨 아래로 이동 | ||
| if (isRefresh) { | ||
| setTimeout(() => { | ||
| window.scrollTo({ top: document.body.scrollHeight, behavior: 'auto' }); | ||
| }, 100); | ||
| } catch (error) { | ||
| console.error('오늘의 한마디 목록 조회 오류:', error); | ||
|
|
||
| let errorMessage = '오늘의 한마디 목록을 불러오는 중 오류가 발생했습니다.'; | ||
|
|
||
| if (error && typeof error === 'object' && 'response' in error) { | ||
| const axiosError = error as { | ||
| response?: { | ||
| data?: { | ||
| message?: string; | ||
| code?: number; | ||
| }; | ||
| }; | ||
| }; | ||
|
|
||
| if (axiosError.response?.data?.message) { | ||
| errorMessage = axiosError.response.data.message; | ||
| } else if (axiosError.response?.data?.code === 403) { | ||
| errorMessage = '방 접근 권한이 없습니다.'; | ||
| } else if (axiosError.response?.data?.code === 404) { | ||
| errorMessage = '존재하지 않는 방입니다.'; | ||
| } | ||
| } | ||
| } else { | ||
|
|
||
| openSnackbar({ | ||
| message: response.message || '오늘의 한마디 목록을 불러오는데 실패했습니다.', | ||
| message: errorMessage, | ||
| variant: 'top', | ||
| onClose: () => {}, | ||
| }); | ||
| } finally { | ||
| setIsLoading(false); | ||
| setIsLoadingMore(false); | ||
| } | ||
| } catch (error) { | ||
| console.error('오늘의 한마디 목록 조회 오류:', error); | ||
|
|
||
| let errorMessage = '오늘의 한마디 목록을 불러오는 중 오류가 발생했습니다.'; | ||
|
|
||
| if (error && typeof error === 'object' && 'response' in error) { | ||
| const axiosError = error as { | ||
| response?: { | ||
| data?: { | ||
| message?: string; | ||
| code?: number; | ||
| }; | ||
| }; | ||
| }; | ||
|
|
||
| if (axiosError.response?.data?.message) { | ||
| errorMessage = axiosError.response.data.message; | ||
| } else if (axiosError.response?.data?.code === 403) { | ||
| errorMessage = '방 접근 권한이 없습니다.'; | ||
| } else if (axiosError.response?.data?.code === 404) { | ||
| errorMessage = '존재하지 않는 방입니다.'; | ||
| } | ||
| } | ||
|
|
||
| openSnackbar({ | ||
| message: errorMessage, | ||
| variant: 'top', | ||
| onClose: () => {}, | ||
| }); | ||
| } finally { | ||
| setIsLoading(false); | ||
| setIsLoadingMore(false); | ||
| } | ||
| }, [roomId, convertToMessage, openSnackbar]); | ||
| }, | ||
| [roomId, convertToMessage, openSnackbar], | ||
| ); |
There was a problem hiding this comment.
loadMessages와 useEffect 의존성으로 인해 불필요한 중복 호출/재시도 루프가 발생할 수 있습니다
현재 구조에서는 다음 문제가 생길 수 있습니다.
loadMessages는useCallback으로 감싸되어 있지만, 의존성 배열에 들어있는convertToMessage는 일반 함수라 렌더마다 새로 생성됩니다.- 그 결과
loadMessages도 매 렌더마다 새로 만들어지고,useEffect(184-188)가loadMessages를 의존성에 포함하고 있어,roomId && !hasInitiallyLoaded조건이 유지되는 동안 효과가 반복 실행될 수 있습니다.- 최초 진입 시: 동일 요청이 2번 이상 호출될 수 있습니다.
- 특히 API 실패로
hasInitiallyLoaded가true로 세팅되지 않는 경우, 렌더가 반복되는 동안 같은 요청을 잦은 간격으로 재시도하는 루프가 생길 여지가 있습니다.
convertToMessage 자체는 외부 상태를 참조하지 않으므로, useCallback으로 한 번 래핑해 참조를 안정화하는 편이 안전해 보입니다.
예시 수정안:
- const convertToMessage = (item: TodayCommentItem): Message => {
- const timeAgo = item.postDate || '방금 전';
-
- const createdAt = new Date();
-
- return {
- id: item.attendanceCheckId.toString(),
- user: item.creatorNickname,
- content: item.todayComment,
- timestamp: item.date,
- timeAgo,
- createdAt,
- profileImageUrl: item.creatorProfileImageUrl,
- isWriter: item.isWriter,
- };
- };
+ const convertToMessage = useCallback(
+ (item: TodayCommentItem): Message => {
+ const timeAgo = item.postDate || '방금 전';
+ const createdAt = new Date();
+
+ return {
+ id: item.attendanceCheckId.toString(),
+ user: item.creatorNickname,
+ content: item.todayComment,
+ timestamp: item.date,
+ timeAgo,
+ createdAt,
+ profileImageUrl: item.creatorProfileImageUrl,
+ isWriter: item.isWriter,
+ };
+ },
+ [],
+ );이렇게 하면:
convertToMessage참조가 안정화되고,loadMessages도roomId/openSnackbar가 바뀌지 않는 한 동일 참조를 유지하게 되어,- 초기 로드/실패 상황에서도 불필요한 중복 호출과 잠재적인 반복 재시도를 막을 수 있습니다.
Committable suggestion skipped: line range outside the PR's diff.
#️⃣ 연관된 이슈
THIP-389
📝 작업 내용
작업 목적
코드베이스 전반에 걸쳐 불필요한 주석을 제거하여 코드 가독성과 유지보수성을 개선했습니다.
작업 배경
작업 내용
1️⃣ API 함수 주석 제거 (a26327e)
src/api/하위 feeds, record, rooms, images 등2️⃣ Components 주석 제거 (0be3cb7)
src/components/전체3️⃣ Hooks 주석 제거 (2bbe02b)
src/hooks/또는src/shared/hooks/4️⃣ Pages 주석 제거 (6fd86e5)
src/pages/5️⃣ Global 설정 주석 제거 (a1ce57e)
src/shared/config/global/6️⃣ Types, Utils 주석 제거 (0c7f553)
src/types/,src/utils/또는src/shared/lib/개선 효과
💬 리뷰 요구사항(선택)
Summary by CodeRabbit
릴리스 노트
새로운 기능
버그 수정
개선사항
✏️ Tip: You can customize this high-level summary in your review settings.