Conversation
Feat/159 mypage UI api
Refactor/162 feedback
…e-api feat: 팔로우 팔로잉 수 구현 및 소식 문의하기 URL 구현
…ribe-ui-api Feat/168 other profile subscribe UI api
Feat/170 news api
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 이 PR은 애플리케이션의 전반적인 성능과 사용자 인터페이스를 개선하는 데 중점을 둡니다. 특히, 데이터 로딩 방식을 무한 스크롤로 전환하여 대량의 콘텐츠를 효율적으로 처리하고, 사용자 상호작용(좋아요, 팔로우)에 대한 반응성을 높였습니다. 또한, 프로필 관련 기능들을 확장하고 UI 요소들의 시각적 완성도를 높여 사용자가 더욱 직관적이고 즐겁게 서비스를 이용할 수 있도록 만들었습니다. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (54)
📝 WalkthroughWalkthroughThis pull request implements server-side state management for user interactions (likes, follows) with optimistic updates, introduces infinite pagination for news and stories, adds nested comment support, creates follow/follower pages for other users, implements book-story UI enhancements with blurred backgrounds, adds club-leaving functionality, and introduces global search state management via a Zustand store. Changes
Sequence DiagramsequenceDiagram
participant User as User/UI
participant Client as Client State
participant Cache as React Query Cache
participant API as Backend API
participant DB as Database
User->>Client: Click like button (book.isbn)
activate Client
Client->>Cache: 1. Cancel in-flight queries
Client->>Cache: 2. Capture previous state<br/>(recommend, detail, searches)
Note over Client,Cache: Optimistic Update Phase
Client->>Cache: 3. Update book.likedByMe<br/>in all caches
Client->>User: Show updated UI instantly
Client->>API: 4. POST /books/{isbn}/like
activate API
API->>DB: Query current state
API->>DB: Toggle like
DB->>API: Return { liked, likes }
API->>Client: Response
deactivate API
activate Cache
Client->>Cache: 5. Apply server response<br/>to primary caches
Client->>Cache: 6. Invalidate search caches<br/>to refresh
deactivate Cache
Client->>User: Show final state
deactivate Client
alt Error
Client->>Cache: Rollback all caches<br/>from captured state
Client->>User: Show error toast
end
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly Related PRs
Suggested Labels
✨ Finishing Touches
🧪 Generate unit tests (beta)
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.
Code Review
This pull request introduces significant refactoring and new features across several pages, primarily focusing on implementing infinite scrolling for various lists and enhancing user interaction. The news and home pages were updated to use useInfiniteNewsQuery and useInfiniteStoriesQuery respectively, replacing static or limited data with dynamically loaded content and react-intersection-observer for infinite scroll. Loading and empty states were added for these lists. The book liking functionality was refactored to use a useToggleBookLikeMutation hook, removing local state management and integrating likedByMe from API responses. A new page for viewing other users' followers and followings was added, complete with tab navigation and infinite scrolling. The 'My Page' follow lists were updated to use useFollowCountQuery for real-time counts. The profile editing page was simplified by removing the ability to change nicknames and the associated duplicate check logic. Story creation was streamlined to only require an ISBN. Additionally, a new useClubInfiniteStoriesQuery was introduced to fetch stories specific to a club. UI improvements include a new useOnClickOutside hook for dropdowns, a password visibility toggle in the login modal, and enhanced image rendering for book covers in story cards. The FloatingFab component now hides when the search modal is open and links to an external inquiry form. Review comments highlighted the need for a comment regarding an unusual isValidSrc check, suggested refactoring the desktop story rendering logic for better maintainability, and pointed out a redundant setPreviewImage call and a confusing handleReplySubmit argument.
Note: Security Review did not run due to the size of the PR.
| const isValidSrc = (src: string) => { | ||
| return src && src !== "string" && (src.startsWith("/") || src.startsWith("http://") || src.startsWith("https://")); | ||
| }; |
There was a problem hiding this comment.
isValidSrc 함수 내의 src !== "string" 조건은 매우 이례적이며, API가 유효하지 않은 데이터를 반환할 수 있음을 시사합니다. 이는 잠재적으로 백엔드의 데이터 품질 문제일 수 있습니다. 코드의 의도를 명확히 하고 향후 유지보수를 위해 이 검사가 왜 필요한지에 대한 주석을 추가하는 것이 좋습니다.
| const isValidSrc = (src: string) => { | |
| return src && src !== "string" && (src.startsWith("/") || src.startsWith("http://") || src.startsWith("https://")); | |
| }; | |
| const isValidSrc = (src: string) => { | |
| // TODO: API에서 이미지 URL로 "string"이라는 문자열이 내려오는 경우가 있어 방어 코드 추가. 추후 백엔드 수정 필요. | |
| return src && src !== "string" && (src.startsWith("/") || src.startsWith("http://") || src.startsWith("https://")); | |
| }; |
| {stories.slice(0, 4).map((s) => renderStory(s, true))} | ||
| {recommendedUsers.length > 0 && ( | ||
| <ListSubscribeLarge | ||
| height="h-[380px]" | ||
| users={recommendedUsers} | ||
| isError={isErrorMembers} | ||
| onSubscribeClick={(nickname, isFollowing) => handleToggleFollow(nickname, isFollowing)} | ||
| /> | ||
| )} | ||
| {stories.slice(4).map((s) => renderStory(s, true))} |
There was a problem hiding this comment.
데스크톱 뷰에서 stories 배열을 slice하여 중간에 ListSubscribeLarge 컴포넌트를 삽입하는 방식은 다소 복잡하고 유지보수가 어렵습니다. 렌더링할 아이템 목록을 미리 준비하는 더 선언적인 방식으로 리팩토링하는 것을 고려해 보세요. 이렇게 하면 코드 가독성이 향상되고 레이아웃 변경에 더 유연하게 대처할 수 있습니다.
예시:
const desktopItems = useMemo(() => {
const storyItems = stories.map(s => ({ type: 'story', data: s, id: s.bookStoryId }));
if (recommendedUsers.length > 0) {
storyItems.splice(4, 0, { type: 'recommend', id: 'recommend-users' });
}
return storyItems;
}, [stories, recommendedUsers]);
// JSX 렌더링 부분
desktopItems.map(item => {
if (item.type === 'story') return renderStory(item.data, true);
if (item.type === 'recommend') return <ListSubscribeLarge ... />;
})| setPreviewImage(user.profileImageUrl || null); | ||
| // Reset check state if it matches original | ||
| setIsNicknameChecked(true); | ||
| setPreviewImage(user.profileImageUrl || null); |
| onKeyDown={(e) => { | ||
| if (e.key === "Enter") { | ||
| handleReplySubmit(comment.parentCommentId || comment.id); | ||
| } | ||
| }} | ||
| placeholder="답글 내용을 입력해주세요" | ||
| className="flex-1 min-w-0 h-[36px] t:h-[56px] px-4 py-3 rounded-lg border border-Subbrown-4 bg-White Body_1_2 text-Gray-7 placeholder:text-Gray-3 outline-none" | ||
| autoFocus | ||
| /> | ||
| <button | ||
| type="button" | ||
| onClick={() => handleReplySubmit(comment.parentCommentId || comment.id)} | ||
| className="px-4 t:px-6 py-2 t:py-3 h-[36px] t:h-[56px] border border-Subbrown-3 text-primary-3 rounded-lg bg-Subbrown-4 subhead_4_1 cursor-pointer shrink-0" | ||
| > | ||
| 입력 |
There was a problem hiding this comment.
handleReplySubmit 함수에 comment.parentCommentId || comment.id를 전달하는 로직이 혼란을 줄 수 있습니다. 현재 UI 구조상 답글 입력창은 최상위 댓글에만 표시되므로 comment.parentCommentId는 항상 null 또는 undefined가 됩니다. 따라서 이 표현식은 항상 comment.id로 귀결됩니다. 코드를 단순화하고 가독성을 높이기 위해 comment.id만 사용하도록 수정하는 것이 좋습니다.
| onKeyDown={(e) => { | |
| if (e.key === "Enter") { | |
| handleReplySubmit(comment.parentCommentId || comment.id); | |
| } | |
| }} | |
| placeholder="답글 내용을 입력해주세요" | |
| className="flex-1 min-w-0 h-[36px] t:h-[56px] px-4 py-3 rounded-lg border border-Subbrown-4 bg-White Body_1_2 text-Gray-7 placeholder:text-Gray-3 outline-none" | |
| autoFocus | |
| /> | |
| <button | |
| type="button" | |
| onClick={() => handleReplySubmit(comment.parentCommentId || comment.id)} | |
| className="px-4 t:px-6 py-2 t:py-3 h-[36px] t:h-[56px] border border-Subbrown-3 text-primary-3 rounded-lg bg-Subbrown-4 subhead_4_1 cursor-pointer shrink-0" | |
| > | |
| 입력 | |
| onKeyDown={(e) => { | |
| if (e.key === "Enter") { | |
| handleReplySubmit(comment.id); | |
| } | |
| }} | |
| placeholder="답글 내용을 입력해주세요" | |
| className="flex-1 min-w-0 h-[36px] t:h-[56px] px-4 py-3 rounded-lg border border-Subbrown-4 bg-White Body_1_2 text-Gray-7 placeholder:text-Gray-3 outline-none" | |
| autoFocus | |
| /> | |
| <button | |
| type="button" | |
| onClick={() => handleReplySubmit(comment.id)} | |
| className="px-4 t:px-6 py-2 t:py-3 h-[36px] t:h-[56px] border border-Subbrown-3 text-primary-3 rounded-lg bg-Subbrown-4 subhead_4_1 cursor-pointer shrink-0" | |
| > | |
| 입력 |
📌 개요 (Summary)
🛠️ 변경 사항 (Changes)
📸 스크린샷 (Screenshots)
(UI 변경 사항이 있다면 첨부해주세요)
✅ 체크리스트 (Checklist)
pnpm build)pnpm lint)Summary by CodeRabbit
Release Notes
New Features
Improvements