Skip to content

03.05 최신화#172

Merged
shinwokkang merged 42 commits intomainfrom
dev
Mar 5, 2026
Merged

03.05 최신화#172
shinwokkang merged 42 commits intomainfrom
dev

Conversation

@shinwokkang
Copy link
Contributor

@shinwokkang shinwokkang commented Mar 5, 2026

📌 개요 (Summary)

  • 변경 사항에 대한 간략한 요약을 적어주세요.
  • 관련 이슈가 있다면 링크를 걸어주세요 (예: [fix] cicd / build 문제 #123).

🛠️ 변경 사항 (Changes)

  • 새로운 기능 추가
  • 버그 수정
  • 코드 리팩토링
  • 문서 업데이트
  • 기타 (설명: )

📸 스크린샷 (Screenshots)

(UI 변경 사항이 있다면 첨부해주세요)

✅ 체크리스트 (Checklist)

  • 빌드가 성공적으로 수행되었나요? (pnpm build)
  • 린트 에러가 없나요? (pnpm lint)
  • 불필요한 콘솔 로그나 주석을 제거했나요?

Summary by CodeRabbit

Release Notes

  • New Features

    • Added global search functionality with improved modal interface
    • Infinite scrolling for news feeds, stories, and book library
    • Book like/unlike with persistent server-side state management
    • Club-based story feeds with separate discovery tabs
    • User follow management with follower/following counts
    • Story sharing via URL clipboard copy
    • Club membership leave option
    • Password visibility toggle in login form
  • Improvements

    • Enhanced book cover displays with layered background effects
    • Improved profile interactions and navigation
    • Better loading and empty states across feeds
    • Optimized mobile/desktop layout separation
    • Enhanced comment reply UI with nested support

shinwokkang and others added 30 commits March 1, 2026 12:34
…e-api

feat: 팔로우 팔로잉 수 구현 및 소식 문의하기 URL 구현
…ribe-ui-api

Feat/168 other profile subscribe UI api
@shinwokkang shinwokkang self-assigned this Mar 5, 2026
@vercel
Copy link

vercel bot commented Mar 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
check-mo Ready Ready Preview, Comment Mar 5, 2026 11:37am

@shinwokkang shinwokkang merged commit 39a3828 into main Mar 5, 2026
3 checks passed
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, 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

  • 무한 스크롤 및 데이터 로딩 개선: 뉴스, 책 이야기, 좋아요 누른 책, 팔로워/팔로잉 목록 등 여러 페이지에 무한 스크롤 기능을 도입하여 사용자 경험을 향상시키고 데이터 로딩 효율성을 높였습니다.
  • 좋아요 및 팔로우 기능 최적화: 책 좋아요 및 사용자 팔로우/언팔로우 기능에 낙관적 업데이트(Optimistic Update)를 적용하여 즉각적인 UI 반응을 제공하고, 관련 캐시를 효율적으로 관리하도록 개선했습니다.
  • 프로필 및 마이페이지 기능 확장: 다른 사용자의 팔로워/팔로잉 목록 페이지를 추가하고, 내 프로필 및 다른 사용자 프로필에서 팔로우/팔로잉 수를 정확하게 표시하며, 모임 탈퇴 기능을 구현했습니다.
  • UI/UX 개선: 로그인 모달에 비밀번호 가시성 토글을 추가하고, 책 이야기 카드 및 뉴스 배너 슬라이더의 시각적 효과를 개선했으며, 검색 모달이 열려 있을 때 플로팅 버튼을 숨기도록 조정했습니다.
  • 빌드 및 API 엔드포인트 관리: 불필요한 빌드 스크립트(build.sh)를 제거하고, 외부 링크를 위한 상수를 추가하며, 다양한 API 엔드포인트 및 서비스 로직을 확장 및 최적화했습니다.

🧠 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
  • build.sh
    • 불필요한 빌드 스크립트가 제거되었습니다.
  • src/app/(main)/books/[id]/page.tsx
    • 책 좋아요 토글 뮤테이션(useToggleBookLikeMutation)이 통합되었고, 로컬 liked 상태가 제거되었습니다.
  • src/app/(main)/news/page.tsx
    • 더미 뉴스 데이터가 useInfiniteNewsQuery를 사용한 무한 스크롤 뉴스 목록으로 대체되었습니다.
    • useInView 훅을 사용하여 뉴스 목록의 지연 로딩이 구현되었습니다.
    • 추천 도서 목록에 likedByMe 속성이 추가되었습니다.
    • Floating Action Button이 외부 문의 양식으로 연결되도록 onClick 핸들러가 추가되었습니다.
  • src/app/(main)/page.tsx
    • 책 이야기 목록에 useInfiniteStoriesQueryuseInView를 활용한 무한 스크롤이 적용되었습니다.
    • 책 이야기 렌더링 로직이 renderStory 헬퍼 함수로 리팩토링되었습니다.
    • 추천 사용자 및 독서 모임 섹션의 데이터 로딩 및 표시 로직이 개선되었습니다.
  • src/app/(main)/profile/[nickname]/follows/page.tsx
    • 다른 사용자의 팔로워/팔로잉 목록을 표시하는 새 페이지가 추가되었습니다.
    • 무한 스크롤 및 팔로우/언팔로우 기능이 구현되었습니다.
  • src/app/(main)/profile/[nickname]/page.tsx
    • ProfileBreadcrumb 컴포넌트에 닉네임 디코딩 로직이 적용되었습니다.
  • src/app/(main)/profile/mypage/follows/page.tsx
    • 팔로워/팔로잉 목록이 useFollowCountQueryuseToggleFollowMutation을 사용하도록 업데이트되었습니다.
    • 더미 데이터 사용이 제거되고 실제 API 데이터로 대체되었습니다.
  • src/app/(main)/setting/profile/page.tsx
    • 닉네임 중복 확인 로직이 제거되었고, 닉네임 입력 필드가 비활성화되어 직접 수정할 수 없도록 변경되었습니다.
    • 프로필 이미지 미리보기 로직이 수정되었습니다.
  • src/app/(main)/stories/new/page.tsx
    • 책 이야기 생성 요청 시 bookInfo 객체 대신 isbn을 직접 사용하도록 간소화되었습니다.
  • src/app/(main)/stories/page.tsx
    • 특정 클럽의 책 이야기를 불러오는 useClubInfiniteStoriesQuery가 추가되었습니다.
  • src/components/base-ui/BookStory/bookstory_card.tsx
    • 프로필 클릭 영역의 UI가 개선되었고, 구독 버튼에 전환 효과가 추가되었습니다.
    • 책 커버 이미지에 시각적 효과(블러, 스케일)가 추가되었습니다.
  • src/components/base-ui/BookStory/bookstory_card_large.tsx
    • 프로필 클릭 영역의 UI가 개선되었고, 구독 버튼에 전환 효과가 추가되었습니다.
    • 책 커버 이미지에 시각적 효과(블러, 스케일)가 추가되었습니다.
  • src/components/base-ui/BookStory/bookstory_detail.tsx
    • 책 이야기 공유 기능이 구현되어 URL을 클립보드에 복사하고 toast 알림을 표시합니다.
    • 공유 버튼의 onClick 핸들러가 handleShare 함수로 연결되었습니다.
  • src/components/base-ui/Comment/comment_list.tsx
    • 댓글 목록에서 답글 입력 시 @닉네임 표시가 추가되었고, 답글 제출 로직이 개선되었습니다.
  • src/components/base-ui/Comment/comment_section.tsx
    • API 응답 데이터를 UI용 댓글 형식으로 변환하고 계층 구조화하는 로직이 개선되어 중첩된 답글을 올바르게 처리합니다.
  • src/components/base-ui/Float.tsx
    • useSearchStore를 통합하여 검색 모달이 열려 있을 때 플로팅 버튼이 숨겨지도록 변경되었습니다.
  • src/components/base-ui/Login/LoginModal.module.css
    • 비밀번호 입력 필드에 대한 스타일 및 토글 버튼 관련 CSS가 추가되었습니다.
  • src/components/base-ui/Login/LoginModal.tsx
    • 로그인 모달에 비밀번호 가시성 토글 기능이 추가되었습니다.
    • 모달 외부 클릭 시 모달이 닫히도록 onClick 핸들러가 추가되었습니다.
  • src/components/base-ui/MyPage/LibraryList.tsx
    • useLikedBooksInfiniteQuery를 사용하여 좋아요 누른 책 목록에 무한 스크롤이 적용되었습니다.
    • 더미 데이터가 제거되고 실제 API 데이터로 대체되었습니다.
  • src/components/base-ui/MyPage/UserProfile.tsx
    • useFollowCountQuery를 사용하여 팔로워/팔로잉 수가 동적으로 표시되도록 업데이트되었습니다.
    • 소식 문의하기 버튼이 외부 문의 양식(EXTERNAL_LINKS.INQUIRY_FORM_URL)으로 연결되었습니다.
  • src/components/base-ui/MyPage/items/MyMeetingCard.tsx
    • 모임 탈퇴 기능이 추가되었고, 확인 모달(ConfirmModal)과 useLeaveClubMutation이 통합되었습니다.
  • src/components/base-ui/News/news_list.tsx
    • 뉴스 목록의 날짜 표시 영역이 반응형으로 개선되었습니다.
  • src/components/base-ui/News/today_recommended_books.tsx
    • 추천 도서 목록에 useToggleBookLikeMutation이 통합되어 좋아요 기능이 작동합니다.
    • 도서 카드 클릭 시 해당 도서 상세 페이지로 이동하는 기능이 추가되었습니다.
  • src/components/base-ui/Profile/LibraryList.tsx
    • useLikedBooksInfiniteQuery를 사용하여 좋아요 누른 책 목록에 무한 스크롤이 적용되었습니다.
    • 다른 사용자의 서재도 조회할 수 있도록 nickname prop이 추가되었습니다.
  • src/components/base-ui/Profile/OtherUser/ProfileBreadcrumb.tsx
    • 다른 사용자 프로필 페이지의 브레드크럼에 닉네임이 동적으로 표시되도록 업데이트되었습니다.
  • src/components/base-ui/Profile/OtherUser/ProfileUserInfo.tsx
    • 팔로우/팔로잉 통계 항목에 해당 목록 페이지로 이동하는 링크가 추가되었습니다.
    • 다른 사용자 프로필의 팔로워 수가 팔로우/언팔로우 액션에 따라 낙관적으로 업데이트되도록 개선되었습니다.
  • src/components/base-ui/Profile/items/LibraryCard.tsx
    • useToggleBookLikeMutation을 사용하여 서재 내 책 좋아요 기능이 구현되었습니다.
    • 책 이미지 소스가 book.imgUrl로 변경되었습니다.
  • src/components/base-ui/home/NewsBannerSlider.tsx
    • 더미 뉴스 배너가 useInfiniteNewsQuery를 통해 불러온 실제 뉴스 데이터로 대체되었습니다.
    • 뉴스 배너 클릭 시 해당 뉴스 상세 페이지로 이동하는 기능이 추가되었습니다.
    • 뉴스 배너 이미지에 시각적 효과 및 텍스트 오버레이가 추가되었습니다.
  • src/components/layout/Header.tsx
    • useSearchStore를 통합하여 검색 모달의 상태를 관리하도록 변경되었습니다.
  • src/components/layout/SearchModal.tsx
    • 검색 모달 내 추천 도서에 useToggleBookLikeMutation이 통합되었습니다.
    • 알라딘 랭킹 링크가 외부 URL(EXTERNAL_LINKS.ALADIN_BESTSELLER_URL)로 연결되었습니다.
    • 검색 결과가 없을 때의 UI가 개선되었습니다.
  • src/constants/links.ts
    • 외부 링크를 관리하기 위한 EXTERNAL_LINKS 상수가 추가되었습니다.
  • src/hooks/mutations/useBookMutations.ts
    • 책 좋아요/취소 기능을 위한 useToggleBookLikeMutation 훅이 추가되었습니다.
    • 낙관적 업데이트 및 관련 쿼리 캐시 무효화 로직이 포함되었습니다.
  • src/hooks/mutations/useClubMutations.ts
    • 모임 탈퇴 기능을 위한 useLeaveClubMutation 훅이 추가되었습니다.
  • src/hooks/mutations/useMemberMutations.ts
    • useToggleFollowMutation 훅에 팔로워/팔로잉 수에 대한 낙관적 업데이트 로직이 추가되었습니다.
  • src/hooks/queries/useBookQueries.ts
    • 좋아요 누른 책 목록을 위한 likedBooks 쿼리 키와 useLikedBooksInfiniteQuery 훅이 추가되었습니다.
  • src/hooks/queries/useMemberQueries.ts
    • 내 팔로우/팔로잉 수를 위한 followCount 쿼리 키와 useFollowCountQuery 훅이 추가되었습니다.
    • 다른 사용자의 팔로워/팔로잉 목록을 조회할 수 있도록 useFollowerListQueryuseFollowingListQuery가 확장되었습니다.
  • src/hooks/queries/useStoryQueries.ts
    • 특정 클럽의 책 이야기를 위한 clubList 쿼리 키와 useClubInfiniteStoriesQuery 훅이 추가되었습니다.
  • src/hooks/useOnClickOutside.ts
    • 컴포넌트 외부 클릭을 감지하는 useOnClickOutside 유틸리티 훅이 추가되었습니다.
  • src/lib/api/endpoints/Clubs.ts
    • 모임 탈퇴를 위한 leave 엔드포인트가 추가되었습니다.
  • src/lib/api/endpoints/book.ts
    • 책 좋아요/취소 및 좋아요 누른 책 목록 조회를 위한 엔드포인트가 추가되었습니다.
  • src/lib/api/endpoints/bookstory.ts
    • 특정 클럽의 책 이야기 조회를 위한 CLUB 엔드포인트가 추가되었습니다.
  • src/lib/api/endpoints/member.ts
    • 다른 사용자의 팔로워/팔로잉 목록 및 내 팔로우 수 조회를 위한 엔드포인트가 추가되었습니다.
  • src/middleware.ts
    • 파일 이름이 src/proxy.ts로 변경되었습니다.
  • src/services/bookService.ts
    • 책 좋아요/취소(toggleLike) 및 좋아요 누른 책 목록 조회(getLikedBooks) 서비스 메서드가 추가되었습니다.
  • src/services/clubService.ts
    • 모임 탈퇴(leaveClub) 서비스 메서드가 추가되었습니다.
  • src/services/memberService.ts
    • 다른 사용자의 팔로워/팔로잉 목록 조회(getFollowerList, getFollowingList) 및 내 팔로우 수 조회(getMyFollowCount) 서비스 메서드가 추가되었습니다.
  • src/services/newsService.ts
    • getNewsList 메서드에서 cursorId 유효성 검사 로직이 개선되었습니다.
  • src/services/storyService.ts
    • 특정 클럽의 책 이야기 조회(getClubStories) 서비스 메서드가 추가되었습니다.
    • 댓글 생성(createComment) 시 parentCommentId를 요청 본문에 포함하도록 수정되었습니다.
  • src/store/useSearchStore.ts
    • 검색 모달의 상태를 관리하는 Zustand 스토어 useSearchStore가 추가되었습니다.
  • src/types/book.ts
    • Book 인터페이스에 likedByMe 속성이 추가되었습니다.
    • MyLikedBooksResponse 인터페이스가 추가되었습니다.
  • src/types/member.ts
    • OtherProfileResponse 인터페이스에 followerCountfollowingCount 속성이 추가되었습니다.
    • FollowCountResponse 인터페이스가 추가되었습니다.
  • src/types/story.ts
    • CommentInfo 인터페이스에 replies 속성이 추가되었습니다.
    • CreateBookStoryRequest 인터페이스가 간소화되었습니다.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a0e41597-6e79-423a-9347-b4dc65a5d5d1

📥 Commits

Reviewing files that changed from the base of the PR and between d53b8a9 and ba79578.

📒 Files selected for processing (54)
  • build.sh
  • src/app/(main)/books/[id]/page.tsx
  • src/app/(main)/news/page.tsx
  • src/app/(main)/page.tsx
  • src/app/(main)/profile/[nickname]/follows/page.tsx
  • src/app/(main)/profile/[nickname]/page.tsx
  • src/app/(main)/profile/mypage/follows/page.tsx
  • src/app/(main)/setting/profile/page.tsx
  • src/app/(main)/stories/new/page.tsx
  • src/app/(main)/stories/page.tsx
  • src/components/base-ui/BookStory/bookstory_card.tsx
  • src/components/base-ui/BookStory/bookstory_card_large.tsx
  • src/components/base-ui/BookStory/bookstory_detail.tsx
  • src/components/base-ui/Comment/comment_list.tsx
  • src/components/base-ui/Comment/comment_section.tsx
  • src/components/base-ui/Float.tsx
  • src/components/base-ui/Login/LoginModal.module.css
  • src/components/base-ui/Login/LoginModal.tsx
  • src/components/base-ui/MyPage/LibraryList.tsx
  • src/components/base-ui/MyPage/UserProfile.tsx
  • src/components/base-ui/MyPage/items/MyMeetingCard.tsx
  • src/components/base-ui/News/news_list.tsx
  • src/components/base-ui/News/today_recommended_books.tsx
  • src/components/base-ui/Profile/LibraryList.tsx
  • src/components/base-ui/Profile/OtherUser/OtherUserProfileTabs.tsx
  • src/components/base-ui/Profile/OtherUser/ProfileBreadcrumb.tsx
  • src/components/base-ui/Profile/OtherUser/ProfileUserInfo.tsx
  • src/components/base-ui/Profile/items/LibraryCard.tsx
  • src/components/base-ui/home/NewsBannerSlider.tsx
  • src/components/layout/Header.tsx
  • src/components/layout/SearchModal.tsx
  • src/constants/links.ts
  • src/hooks/mutations/useBookMutations.ts
  • src/hooks/mutations/useClubMutations.ts
  • src/hooks/mutations/useMemberMutations.ts
  • src/hooks/mutations/useStoryMutations.ts
  • src/hooks/queries/useBookQueries.ts
  • src/hooks/queries/useMemberQueries.ts
  • src/hooks/queries/useStoryQueries.ts
  • src/hooks/useOnClickOutside.ts
  • src/lib/api/endpoints/Clubs.ts
  • src/lib/api/endpoints/book.ts
  • src/lib/api/endpoints/bookstory.ts
  • src/lib/api/endpoints/member.ts
  • src/proxy.ts
  • src/services/bookService.ts
  • src/services/clubService.ts
  • src/services/memberService.ts
  • src/services/newsService.ts
  • src/services/storyService.ts
  • src/store/useSearchStore.ts
  • src/types/book.ts
  • src/types/member.ts
  • src/types/story.ts

📝 Walkthrough

Walkthrough

This 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

Cohort / File(s) Summary
Build Script Removal
build.sh
Deleted shell script that performed directory navigation and bidirectional content copying.
Server-Side Book Likes
src/app/(main)/books/[id]/page.tsx, src/components/base-ui/MyPage/LibraryList.tsx, src/components/base-ui/MyPage/items/LibraryCard.tsx, src/components/base-ui/News/today_recommended_books.tsx, src/components/base-ui/Profile/items/LibraryCard.tsx, src/components/layout/SearchModal.tsx
Replaced local like state with server-driven mutations; uses likedByMe field from server and useToggleBookLikeMutation for toggles with optimistic updates across multiple query caches.
Infinite Pagination - News & Stories
src/app/(main)/news/page.tsx, src/app/(main)/page.tsx, src/app/(main)/stories/page.tsx, src/components/base-ui/home/NewsBannerSlider.tsx, src/components/base-ui/MyPage/LibraryList.tsx, src/components/base-ui/Profile/LibraryList.tsx
Implements infinite scroll using useInfiniteQuery hooks with useInView sentinel detection; fetches paginated data and triggers fetchNextPage when scrolled into view. Supports club-specific story filtering via new useClubInfiniteStoriesQuery.
Book Story UI Enhancements
src/components/base-ui/BookStory/bookstory_card.tsx, src/components/base-ui/BookStory/bookstory_card_large.tsx
Restructured profile sections with nested hover interactions; enhanced book cover with blurred background beneath foreground image; updated subscribe button styling with transition-all and active states.
Other User Follow Pages
src/app/(main)/profile/[nickname]/follows/page.tsx
New page component for viewing other users' followers/following lists with tab-based navigation; fetches profile and follow data via queries; renders paginated follow lists with toggle-follow capability.
Profile & Navigation Updates
src/app/(main)/profile/[nickname]/page.tsx, src/components/base-ui/Profile/OtherUser/ProfileBreadcrumb.tsx, src/components/base-ui/Profile/OtherUser/ProfileUserInfo.tsx, src/components/base-ui/Profile/OtherUser/OtherUserProfileTabs.tsx
Added nickname decoding and dynamic breadcrumb labels; updated profile stats to use real follow counts from queries; made stats clickable links to follow pages; renamed and updated component signatures.
Comment Threading Support
src/components/base-ui/Comment/comment_list.tsx, src/components/base-ui/Comment/comment_section.tsx
Extended Comment type with optional parentCommentId; implemented recursive comment mapping in comment_section.tsx to build nested reply structure; refactored reply UI with headers and improved visual layout.
Login Modal & Authentication
src/components/base-ui/Login/LoginModal.tsx, src/components/base-ui/Login/LoginModal.module.css
Added password visibility toggle with eye icons; implemented overlay click-to-close behavior; added onSignUp callback prop; introduced .passwordWrapper, .passwordToggle CSS classes and updated input text color.
Global Search State
src/components/layout/Header.tsx, src/components/base-ui/Float.tsx, src/store/useSearchStore.ts
Created useSearchStore Zustand store to manage isSearchOpen state globally; updated Header and FloatingFab to use store actions instead of local state; FAB now hides when search is open.
Club Management
src/app/(main)/profile/mypage/follows/page.tsx, src/components/base-ui/MyPage/items/MyMeetingCard.tsx
Added ability to leave clubs with confirmation modal; updated follow count display to use real data from useFollowCountQuery; replaced dummy counts with dynamic values.
Nickname & Follow Count Queries
src/app/(main)/profile/mypage/follows/page.tsx, src/app/(main)/setting/profile/page.tsx, src/components/base-ui/MyPage/UserProfile.tsx
Simplified profile edit page by disabling nickname modification and showing message "닉네임 수정은 따로 문의해주세요!"; updated follow list queries to accept optional nickname parameter; added follow count fetching for my profile.
Book Story Mutations & Sharing
src/app/(main)/stories/new/page.tsx, src/components/base-ui/BookStory/bookstory_detail.tsx
Simplified create story payload from nested bookInfo to flat structure (isbn, title, description); added URL-to-clipboard sharing with toast feedback via react-hot-toast.
API Endpoints & Services
src/lib/api/endpoints/book.ts, src/lib/api/endpoints/bookstory.ts, src/lib/api/endpoints/member.ts, src/lib/api/endpoints/Clubs.ts, src/services/bookService.ts, src/services/memberService.ts, src/services/clubService.ts, src/services/storyService.ts, src/services/newsService.ts
Added new endpoints: book likes (LIKE, MY_LIKES, OTHER_LIKES), club-stories (CLUB), member follows (GET_OTHER_FOLLOWERS, GET_OTHER_FOLLOWINGS, GET_FOLLOW_COUNT), club leave endpoint; implemented corresponding service methods with proper type handling and cursor-based pagination.
Query Hooks
src/hooks/queries/useBookQueries.ts, src/hooks/queries/useMemberQueries.ts, src/hooks/queries/useStoryQueries.ts
Added new query key generators and hooks: useLikedBooksInfiniteQuery, useFollowCountQuery, updated useFollowerListQuery and useFollowingListQuery to support optional nickname parameter, added useClubInfiniteStoriesQuery for club-specific stories.
Mutation Hooks
src/hooks/mutations/useBookMutations.ts, src/hooks/mutations/useMemberMutations.ts, src/hooks/mutations/useClubMutations.ts, src/hooks/mutations/useStoryMutations.ts
Implemented useToggleBookLikeMutation with throttling and comprehensive optimistic updates across recommend, detail, infinite search, and liked-books caches; enhanced member follow mutations to track follow counts; added useLeaveClubMutation; improved story like mutation type safety.
Utility Hooks & Store
src/hooks/useOnClickOutside.ts
New hook for detecting clicks outside a ref element; supports both mouse and touch events with cleanup on unmount.
Type Definitions
src/types/book.ts, src/types/member.ts, src/types/story.ts
Extended Book with optional likedByMe; extended BookSearchResponse with optional nextCursor; added MyLikedBooksResponse interface; replaced categories with followerCount and followingCount in OtherProfileResponse; added FollowCountResponse interface; extended CommentInfo with optional nested replies; simplified CreateBookStoryRequest from nested to flat structure.
Middleware
src/proxy.ts
Renamed exported middleware function from middleware to proxy; no behavioral changes.
News & External Links
src/constants/links.ts, src/components/base-ui/News/news_list.tsx, src/components/base-ui/MyPage/UserProfile.tsx
Added EXTERNAL_LINKS constant with inquiry form and Aladdin bestseller URLs; integrated external inquiry link in UserProfile; updated news date container layout with flexible width and whitespace handling.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly Related PRs

Suggested Labels

✨ feat, 🎨 ui, ⚡ refactor


🐰 Hops through infinite scroll streams,
Likes now live on server dreams,
Comments nested, follows gleam,
UI blooms with blurred sunbeams!

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

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.

Comment on lines +44 to +46
const isValidSrc = (src: string) => {
return src && src !== "string" && (src.startsWith("/") || src.startsWith("http://") || src.startsWith("https://"));
};
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

isValidSrc 함수 내의 src !== "string" 조건은 매우 이례적이며, API가 유효하지 않은 데이터를 반환할 수 있음을 시사합니다. 이는 잠재적으로 백엔드의 데이터 품질 문제일 수 있습니다. 코드의 의도를 명확히 하고 향후 유지보수를 위해 이 검사가 왜 필요한지에 대한 주석을 추가하는 것이 좋습니다.

Suggested change
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://"));
};

Comment on lines +217 to +226
{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))}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

데스크톱 뷰에서 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);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

setPreviewImage 호출이 중복되었습니다. 불필요한 라인을 제거해주세요.

Comment on lines +137 to +151
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"
>
입력
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

handleReplySubmit 함수에 comment.parentCommentId || comment.id를 전달하는 로직이 혼란을 줄 수 있습니다. 현재 UI 구조상 답글 입력창은 최상위 댓글에만 표시되므로 comment.parentCommentId는 항상 null 또는 undefined가 됩니다. 따라서 이 표현식은 항상 comment.id로 귀결됩니다. 코드를 단순화하고 가독성을 높이기 위해 comment.id만 사용하도록 수정하는 것이 좋습니다.

Suggested change
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"
>
입력

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants