Skip to content

Feat 108 group api#130

Closed
hongik-luke wants to merge 71 commits intomainfrom
feat-108-group-api
Closed

Feat 108 group api#130
hongik-luke wants to merge 71 commits intomainfrom
feat-108-group-api

Conversation

@hongik-luke
Copy link
Contributor

@hongik-luke hongik-luke commented Feb 25, 2026

📌 개요 (Summary)

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

🛠️ 변경 사항 (Changes)

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

📸 스크린샷 (Screenshots)

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

✅ 체크리스트 (Checklist)

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

Summary by CodeRabbit

Release Notes

  • New Features

    • Real-time data fetching with infinite scroll for books, stories, and search results
    • Admin team management with drag-and-drop member assignment
    • Multi-step club creation wizard with image upload and name validation
    • Comment editing capability for story discussions
    • Book recommendations and dynamic search-driven results
  • Refactor

    • Integrated API data layer replacing mock data throughout the app
    • Added query caching and centralized state management
  • Chores

    • Added drag-and-drop and query management libraries

김기현 and others added 30 commits February 19, 2026 20:45
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@hongik-luke hongik-luke linked an issue Feb 25, 2026 that may be closed by this pull request
4 tasks
@vercel
Copy link

vercel bot commented Feb 25, 2026

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

Project Deployment Actions Updated (UTC)
check-mo Building Building Preview Feb 25, 2026 1:48pm

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4327ca and 5530e7f.

⛔ Files ignored due to path filters (5)
  • package-lock.json is excluded by !**/package-lock.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • public/ArrowLeft3.svg is excluded by !**/*.svg
  • public/Polygon7.svg is excluded by !**/*.svg
  • public/icon_plus_2.svg is excluded by !**/*.svg
📒 Files selected for processing (86)
  • .env.example
  • .gitignore
  • next.config.ts
  • package.json
  • src/app/(main)/books/[id]/page.tsx
  • src/app/(main)/page.tsx
  • src/app/(main)/search/page.tsx
  • src/app/(main)/stories/[id]/page.tsx
  • src/app/(main)/stories/new/page.tsx
  • src/app/(main)/stories/page.tsx
  • src/app/groups/[id]/admin/bookcase/[meetingId]/dummy.ts
  • src/app/groups/[id]/admin/bookcase/[meetingId]/layout.tsx
  • src/app/groups/[id]/admin/bookcase/[meetingId]/page.tsx
  • src/app/groups/[id]/admin/bookcase/new/page.tsx
  • src/app/groups/[id]/admin/bookcase/page.tsx
  • src/app/groups/[id]/bookcase/[bookId]/MeetingTabSection.tsx
  • src/app/groups/[id]/bookcase/[bookId]/meeting/page.tsx
  • src/app/groups/[id]/bookcase/[bookId]/page.tsx
  • src/app/groups/[id]/bookcase/page.tsx
  • src/app/groups/[id]/layout.tsx
  • src/app/groups/[id]/page.tsx
  • src/app/groups/create/page.tsx
  • src/app/groups/groupSearchDummy.ts
  • src/app/groups/page.tsx
  • src/app/layout.tsx
  • src/app/providers.tsx
  • src/components/base-ui/BookStory/bookstory_card.tsx
  • src/components/base-ui/BookStory/bookstory_card_large.tsx
  • src/components/base-ui/Bookcase/Admin/.gitkeep
  • src/components/base-ui/Bookcase/Admin/bookdetailgrouping/MemberItem.tsx
  • src/components/base-ui/Bookcase/Admin/bookdetailgrouping/MemberPool.tsx
  • src/components/base-ui/Bookcase/Admin/bookdetailgrouping/TeamBoard.tsx
  • src/components/base-ui/Bookcase/BookDetailNav.tsx
  • src/components/base-ui/Bookcase/BookcaseCard.tsx
  • src/components/base-ui/Bookcase/ChatTeamSelectModal.tsx
  • src/components/base-ui/Bookcase/MeetingInfo.tsx
  • src/components/base-ui/Comment/comment_item.tsx
  • src/components/base-ui/Comment/comment_section.tsx
  • src/components/base-ui/Group-Search/search_club_apply_modal.tsx
  • src/components/base-ui/Group-Search/search_clublist/search_clublist_item.tsx
  • src/components/base-ui/Group-Search/search_mybookclub.tsx
  • src/components/base-ui/Group/BookshelfModal.tsx
  • src/components/base-ui/Login/useLoginForm.tsx
  • src/components/base-ui/home/list_subscribe_element.tsx
  • src/components/base-ui/home/list_subscribe_large.tsx
  • src/components/common/ConfirmModal.tsx
  • src/components/layout/BookSelectModal.tsx
  • src/components/layout/SearchModal.tsx
  • src/hooks/mutations/useCreateClubMutation.ts
  • src/hooks/mutations/useSearchClubMutations.ts
  • src/hooks/mutations/useStoryMutations.ts
  • src/hooks/queries/useBookQueries.ts
  • src/hooks/queries/useCreateClubQueries.ts
  • src/hooks/queries/useMemberQueries.ts
  • src/hooks/queries/useSearchClubQueries.ts
  • src/hooks/queries/useStoryQueries.ts
  • src/hooks/useDebounce.ts
  • src/lib/api/client.ts
  • src/lib/api/client/index.ts
  • src/lib/api/endpoints/Clubs.ts
  • src/lib/api/endpoints/Image.ts
  • src/lib/api/endpoints/auth.ts
  • src/lib/api/endpoints/base.ts
  • src/lib/api/endpoints/book.ts
  • src/lib/api/endpoints/bookstory.ts
  • src/lib/api/endpoints/index.ts
  • src/lib/api/endpoints/member.ts
  • src/lib/api/errors/ApiError.ts
  • src/lib/api/errors/errorMapper.ts
  • src/lib/api/errors/index.ts
  • src/lib/api/types.ts
  • src/services/bookService.ts
  • src/services/clubService.ts
  • src/services/imageService.ts
  • src/services/memberService.ts
  • src/services/storyService.ts
  • src/types/book.ts
  • src/types/groups/bookcasedetail.ts
  • src/types/groups/bookcasehome.ts
  • src/types/groups/clubCreate.ts
  • src/types/groups/clubsearch.ts
  • src/types/member.ts
  • src/types/story.ts
  • src/utils/groupMapper.ts
  • src/utils/time.ts
  • src/utils/url.ts

📝 Walkthrough

Walkthrough

This PR introduces a comprehensive data fetching and integration layer, replacing static dummy data with real API calls. It adds React Query infrastructure, multiple API services, data transformation hooks, drag-and-drop team management, infinite scrolling pagination, and client-side API client with error handling across numerous pages and components.

Changes

Cohort / File(s) Summary
Configuration & Environment
.env.example, .gitignore, next.config.ts, package.json
Added API URL environment variable, .env.example exception, remote image patterns for Aladin and S3, and new dependencies (dnd-kit, react-query, react-intersection-observer).
API Infrastructure
src/lib/api/client.ts, src/lib/api/client/index.ts, src/lib/api/errors/..., src/lib/api/endpoints/..., src/lib/api/types.ts
Established HTTP client with GET/POST/PUT/DELETE/PATCH methods, timeout handling, 401 session management, and standardized error handling. Centralized endpoints for auth, books, clubs, stories, images. Added ApiError class and error message mapping.
Services Layer
src/services/*.ts
Created service wrappers (bookService, clubService, storyService, memberService, imageService) delegating to apiClient with typed responses and result extraction.
React Query Hooks
src/hooks/queries/..., src/hooks/mutations/..., src/hooks/useDebounce.ts
Added query hooks for books, stories, clubs, members with pagination support; mutation hooks for story/comment/club operations; debounce utility for search inputs.
Type Definitions
src/types/..., src/utils/time.ts, src/utils/url.ts
Defined API request/response types (Book, BookStory, CommentInfo, Club, Member), helper types for bookcase team management, URL/time validation utilities.
Home & Story Pages
src/app/(main)/page.tsx, src/app/(main)/stories/page.tsx, src/app/(main)/stories/[id]/page.tsx, src/app/(main)/stories/new/page.tsx
Integrated data queries and mutations; replaced dummy data with useStoriesQuery, useRecommendedMembersQuery, useInfiniteStoriesQuery; added loading/error states; connected create/edit flows to storyService mutations.
Book & Search Pages
src/app/(main)/books/[id]/page.tsx, src/app/(main)/search/page.tsx
Replaced hardcoded book data with useBookDetailQuery; integrated infinite pagination via useInfiniteBookSearchQuery with intersection observer triggers; updated navigation to use ISBN instead of IDs.
Groups & Bookcase Pages
src/app/groups/page.tsx, src/app/groups/create/page.tsx, src/app/groups/[id]/..., src/app/groups/[id]/admin/bookcase/..., src/app/groups/[id]/bookcase/...
Added club search with filters and infinite scroll; created club signup wizard with image upload and mutations; implemented admin meeting team management with drag-and-drop; replaced bookcase list rendering with API-driven data.
Drag-and-Drop Components
src/components/base-ui/Bookcase/Admin/bookdetailgrouping/..., src/app/groups/[id]/admin/bookcase/[meetingId]/page.tsx
Introduced MemberItem, MemberPool, TeamBoard components using dnd-kit for managing team member assignments; added drag event handling with data payloads and visual feedback.
Bookcase & Meeting Components
src/components/base-ui/Bookcase/BookDetailNav.tsx, src/components/base-ui/Bookcase/BookcaseCard.tsx, src/components/base-ui/Bookcase/MeetingInfo.tsx, src/components/base-ui/Bookcase/ChatTeamSelectModal.tsx, src/app/groups/[id]/bookcase/[bookId]/...
Updated BookDetailNav to export Tab type; made BookcaseCard.imageUrl required; enhanced MeetingInfo with optional meetingId/isAdmin and conditional team management button; added ChatTeamSelectModal for team selection UI; refactored bookcase detail page with tab-driven routing.
List & Search UI Components
src/components/base-ui/Group-Search/..., src/components/base-ui/home/..., src/components/layout/BookSelectModal.tsx, src/components/layout/SearchModal.tsx
Updated ListSubscribeElement/ListSubscribeLarge to accept optional user counts and users array; added isLoading prop to search_mybookclub; added ClubSummary export; updated BookSelectModal and SearchModal with debounced search and infinite scroll integration.
Comment & Base Components
src/components/base-ui/Comment/..., src/components/base-ui/BookStory/..., src/components/common/ConfirmModal.tsx
Added comment editing mode with save/cancel flow; integrated API mutations for create/update/delete with confirmation modal; replaced timeAgo with formatTimeAgo utility; added CommentSection support for initial comments and author nickname.
Removed/Cleanup
src/app/groups/groupSearchDummy.ts, src/app/groups/[id]/admin/bookcase/page.tsx
Removed static dummy group and club data; removed old admin bookcase listing page (replaced by new admin team management).
App Setup
src/app/layout.tsx, src/app/providers.tsx
Introduced Providers wrapper component with TanStack Query client configuration (60s staleTime, no focus refetch, 1 retry) and ReactQueryDevtools integration.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

✨ feat, 📦 build

Poem

🐰 Hops with glee through API calls,
Infinite scrolls and data halls,
Drag-and-drop with dnd-kit cheer,
React Query brings data near!
From dummy land to real-world streams,
This rabbit code now lives the dreams! 🚀

✨ 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 feat-108-group-api

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.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @hongik-luke, 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!

This pull request significantly advances the application's functionality by integrating numerous backend APIs, moving away from dummy data for core features. It focuses on enhancing data-driven interactions, improving user experience through dynamic content loading and interactive elements, and laying robust groundwork for future group-related features and content management.

Highlights

  • API Integration & Data Fetching: Implemented comprehensive API integration across various features including book search, book stories, club management, and member recommendations. This involved creating new service and endpoint files, and utilizing React Query for efficient data fetching and state management.
  • Enhanced User Experience: Introduced infinite scrolling for book search results and book stories listings, improving performance and user navigation. Dynamic loading states and error handling have been added for a smoother user experience.
  • Club Creation & Management: Refactored the club creation wizard to integrate with backend APIs for name duplication checks, profile image uploads, and final club creation. A new DND-kit based team management interface was added for group meetings.
  • Book Story & Comment Functionality: Migrated book story details and comment sections from dummy data to live API calls. Added features for creating, editing, and deleting comments, along with a confirmation modal for deletions.
  • Infrastructure & Utilities: Configured Next.js remote image patterns, added environment variables for API endpoints, and introduced utility hooks like useDebounce and helper functions for time formatting and URL validation.

🧠 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
  • .env.example
    • Added NEXT_PUBLIC_API_URL for API endpoint configuration.
  • .gitignore
    • Updated to explicitly include .env.example while keeping .env ignored.
  • next.config.ts
    • Configured remotePatterns for Next.js Image component to allow images from image.aladin.co.kr and checkmo-s3-presigned.s3.ap-northeast-2.amazonaws.com.
  • package-lock.json
    • Added @dnd-kit/accessibility, @dnd-kit/core, @dnd-kit/utilities for drag-and-drop functionality.
    • Added @tanstack/query-core, @tanstack/query-devtools, @tanstack/react-query, @tanstack/react-query-devtools for React Query integration.
    • Added react-intersection-observer for infinite scrolling.
    • Updated dependencies to reflect new package additions.
  • package.json
    • Added @dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities for drag-and-drop functionality.
    • Added @tanstack/react-query, @tanstack/react-query-devtools for React Query integration.
    • Added react-intersection-observer for infinite scrolling.
  • pnpm-lock.yaml
    • Updated lockfile to include new @dnd-kit packages and their dependencies.
    • Updated lockfile to include new @tanstack/react-query packages and their dependencies.
    • Updated lockfile to include react-intersection-observer and its dependencies.
  • public/ArrowLeft3.svg
    • Added new SVG icon for a left arrow.
  • public/Polygon7.svg
    • Added new SVG icon for a polygon shape.
  • public/icon_plus_2.svg
    • Added new SVG icon for a plus sign.
  • src/app/(main)/books/[id]/page.tsx
    • Replaced dummy book data with useBookDetailQuery for dynamic fetching.
    • Updated onPencilClick to use isbn instead of bookId.
    • Added loading and error states for book detail fetching.
  • src/app/(main)/page.tsx
    • Replaced dummy story data with useStoriesQuery and useRecommendedMembersQuery.
    • Implemented loading spinners for data fetching.
    • Updated ListSubscribeElement and BookStoryCardLarge to use fetched data structure.
  • src/app/(main)/search/page.tsx
    • Implemented infinite scrolling for book search using useInfiniteBookSearchQuery and useInView.
    • Replaced dummy search results with API-fetched data.
    • Updated SearchBookResult to use isbn as key and description for detail.
  • src/app/(main)/stories/[id]/page.tsx
    • Replaced static story data with useStoryDetailQuery for dynamic story details.
    • Added loading and 404 error states for story fetching.
    • Updated StoryNavigation and BookstoryDetail to use API-fetched story properties.
    • Integrated CommentSection with initial comments and story author nickname from API data.
  • src/app/(main)/stories/new/page.tsx
    • Integrated useBookDetailQuery to fetch selected book details by ISBN.
    • Implemented useCreateBookStoryMutation for submitting new book stories.
    • Added toast notifications for success/failure of story creation.
    • Updated book selection to use ISBN instead of book ID.
  • src/app/(main)/stories/page.tsx
    • Implemented infinite scrolling for book stories using useInfiniteStoriesQuery and useInView.
    • Integrated useRecommendedMembersQuery for member recommendations.
    • Replaced dummy story data with API-fetched stories.
    • Added loading states for stories and recommended members.
  • src/app/groups/[id]/admin/bookcase/[meetingId]/dummy.ts
    • Added new dummy data for meeting teams to simulate API responses.
  • src/app/groups/[id]/admin/bookcase/[meetingId]/layout.tsx
    • Added new layout file for admin meeting team management page.
  • src/app/groups/[id]/admin/bookcase/[meetingId]/page.tsx
    • Added new page for DND-kit based team management within a meeting.
    • Implemented drag-and-drop functionality for assigning members to teams.
    • Integrated useDraggable and useDroppable from @dnd-kit/core.
    • Added functionality to add and remove teams, and move members between teams or to an unassigned pool.
  • src/app/groups/[id]/admin/bookcase/new/page.tsx
    • Updated book selection to use isbn parameter and useBookDetailQuery.
  • src/app/groups/[id]/admin/bookcase/page.tsx
    • Removed the file, indicating a refactor or removal of this specific admin bookcase listing page.
  • src/app/groups/[id]/bookcase/[bookId]/MeetingTabSection.tsx
    • Added new component to display meeting information and team details within the book detail page.
    • Includes mock data for meeting details and team members.
  • src/app/groups/[id]/bookcase/[bookId]/meeting/page.tsx
    • Added ChatTeamSelectModal for chat team selection.
    • Updated sorting logic for debate items.
    • Refactored UI elements for better responsiveness and interactivity.
  • src/app/groups/[id]/bookcase/[bookId]/page.tsx
    • Refactored to use MeetingTabSection for meeting-related content.
    • Implemented URL-based tab management using useSearchParams and useRouter.
    • Updated BookDetailNav to use new tab types (topic, review, meeting).
    • Added handleManageTeams function to navigate to the admin team management page.
  • src/app/groups/[id]/bookcase/page.tsx
    • Updated to use BookcaseApiResponse and groupByGeneration utility for displaying book cases.
    • Replaced dummy data with API-like mock data for book cases.
    • Modified handleGoToDetail to navigate to book detail page with specific tab parameters.
  • src/app/groups/[id]/layout.tsx
    • Adjusted layout exclusion logic to apply to all admin bookcase pages, not just the new creation page.
  • src/app/groups/[id]/page.tsx
    • Updated the '이번 모임 바로가기' button to navigate to a specific notice page.
  • src/app/groups/create/page.tsx
    • Integrated useClubNameCheckQuery for real-time club name duplication checks.
    • Integrated useUploadClubImageMutation for uploading club profile images to S3.
    • Integrated useCreateClubMutation for submitting new club creation requests.
    • Added toast notifications for various actions (name check, image upload, club creation).
    • Refactored canNext logic to account for API call states and required fields.
  • src/app/groups/groupSearchDummy.ts
    • Removed the dummy data file.
  • src/app/groups/page.tsx
    • Integrated useMyClubsQuery, useClubRecommendationsQuery, and useInfiniteClubSearchQuery for dynamic club data.
    • Implemented infinite scrolling for club search results using useInView.
    • Updated Mybookclub and SearchClubListItem components to use API-fetched data and new types.
    • Integrated useClubJoinMutation for handling club join requests.
  • src/app/layout.tsx
    • Wrapped the application with Providers for React Query context.
  • src/app/providers.tsx
    • Added new file to configure and provide QueryClientProvider and ReactQueryDevtools for the application.
  • src/components/base-ui/BookStory/bookstory_card.tsx
    • Updated time formatting logic to use formatTimeAgo utility.
  • src/components/base-ui/BookStory/bookstory_card_large.tsx
    • Updated time formatting logic to use formatTimeAgo utility.
  • src/components/base-ui/Bookcase/Admin/bookdetailgrouping/MemberItem.tsx
    • Added new component for displaying a draggable member item in team management.
  • src/components/base-ui/Bookcase/Admin/bookdetailgrouping/MemberPool.tsx
    • Added new component for the unassigned member pool in team management, with drag-and-drop support and a confirmation modal.
  • src/components/base-ui/Bookcase/Admin/bookdetailgrouping/TeamBoard.tsx
    • Added new component for displaying and managing teams, with drag-and-drop support for members and team add/remove functionality.
  • src/components/base-ui/Bookcase/BookDetailNav.tsx
    • Refactored tab types from Korean strings to English enums (topic, review, meeting) and added a label mapping.
  • src/components/base-ui/Bookcase/BookcaseCard.tsx
    • Updated imageUrl prop to be required.
  • src/components/base-ui/Bookcase/ChatTeamSelectModal.tsx
    • Added new modal component for selecting a chat team, with draggable functionality on tablet/desktop.
  • src/components/base-ui/Bookcase/MeetingInfo.tsx
    • Added isAdmin prop to conditionally render the '조 관리하기' button.
    • Updated styling and layout for meeting information.
  • src/components/base-ui/Comment/comment_item.tsx
    • Added functionality for editing and canceling comment edits.
    • Updated menu options to include edit and delete for own comments, and report for others.
  • src/components/base-ui/Comment/comment_section.tsx
    • Integrated useCreateCommentMutation, useUpdateCommentMutation, and useDeleteCommentMutation for comment management.
    • Added ConfirmModal for comment deletion confirmation.
    • Mapped API comment data to UI comment structure, including nested replies and sorting.
  • src/components/base-ui/Group-Search/search_club_apply_modal.tsx
    • Updated styling for rounded corners and button text.
    • Removed w-[132px] from apply button to allow dynamic width.
  • src/components/base-ui/Group-Search/search_clublist/search_clublist_item.tsx
    • Refactored ClubSummary type to be defined within the component file.
    • Added safeImageSrc utility for handling various image URL formats.
    • Updated styling and layout for mobile and tablet views, including button widths and hover effects.
  • src/components/base-ui/Group-Search/search_mybookclub.tsx
    • Added isLoading prop to display a loading state.
    • Updated showToggle logic to consider loading state.
    • Added router.push to navigate to group detail page on click.
  • src/components/base-ui/Group/BookshelfModal.tsx
    • Added imageUrl prop to BookcaseCard component within the modal.
  • src/components/base-ui/Login/useLoginForm.tsx
    • Updated import path for ApiError.
  • src/components/base-ui/home/list_subscribe_element.tsx
    • Made subscribingCount and subscribersCount props optional and conditionally rendered them.
  • src/components/base-ui/home/list_subscribe_large.tsx
    • Made subscribingCount and subscribersCount props optional and conditionally rendered them.
    • Added users prop to allow passing external user data, with a fallback to dummy data.
  • src/components/common/ConfirmModal.tsx
    • Added new reusable modal component for confirmation dialogs, with keyboard escape and overlay click to close.
  • src/components/layout/BookSelectModal.tsx
    • Integrated useInfiniteBookSearchQuery and useDebounce for real-time, infinite-scrolling book search.
    • Replaced dummy book data with API-fetched results.
    • Added loading states and useInView for infinite scroll trigger.
  • src/components/layout/SearchModal.tsx
    • Integrated useInfiniteBookSearchQuery, useRecommendedBooksQuery, and useDebounce for dynamic search and recommendations.
    • Implemented infinite scrolling for search results.
    • Added loading states for search and recommended books.
  • src/hooks/mutations/useCreateClubMutation.ts
    • Added new file for React Query mutations related to club creation and image uploads.
  • src/hooks/mutations/useSearchClubMutations.ts
    • Added new file for React Query mutation related to club join requests.
  • src/hooks/mutations/useStoryMutations.ts
    • Added new file for React Query mutations related to book story and comment creation, update, and deletion.
  • src/hooks/queries/useBookQueries.ts
    • Added new file for React Query hooks to fetch book search results, recommended books, and book details.
  • src/hooks/queries/useCreateClubQueries.ts
    • Added new file for React Query hook to check club name duplication.
  • src/hooks/queries/useMemberQueries.ts
    • Added new file for React Query hook to fetch recommended members.
  • src/hooks/queries/useSearchClubQueries.ts
    • Added new file for React Query hooks to fetch user's clubs, club recommendations, and infinite club search results.
  • src/hooks/queries/useStoryQueries.ts
    • Added new file for React Query hooks to fetch all stories, story details, and infinite story lists.
  • src/hooks/useDebounce.ts
    • Added new utility hook for debouncing values.
  • src/lib/api/client.ts
    • Added patch method to the apiClient for partial updates.
  • src/lib/api/client/index.ts
    • Added new API client with interceptors for authentication, error handling, and request timeouts.
  • src/lib/api/endpoints/Clubs.ts
    • Added new endpoint definitions for club-related APIs (create, check name, my clubs, recommendations, search, join).
  • src/lib/api/endpoints/Image.ts
    • Added new endpoint definitions for image upload APIs.
  • src/lib/api/endpoints/auth.ts
    • Added new endpoint definitions for authentication-related APIs.
  • src/lib/api/endpoints/base.ts
    • Added new base endpoint definition, reading NEXT_PUBLIC_API_URL from environment variables.
  • src/lib/api/endpoints/book.ts
    • Added new endpoint definitions for book-related APIs (search, recommend, detail).
  • src/lib/api/endpoints/bookstory.ts
    • Added new endpoint definitions for book story-related APIs.
  • src/lib/api/endpoints/index.ts
    • Exported all new endpoint definitions.
  • src/lib/api/endpoints/member.ts
    • Added new endpoint definitions for member-related APIs (recommend).
  • src/lib/api/errors/ApiError.ts
    • Added new custom ApiError class for standardized error handling.
  • src/lib/api/errors/errorMapper.ts
    • Added new utility for mapping API error codes to user-friendly messages.
  • src/lib/api/errors/index.ts
    • Exported ApiError and getErrorMessage.
  • src/lib/api/types.ts
    • Added new generic ApiResponse type for standardized API responses.
  • src/services/bookService.ts
    • Added new service for interacting with book-related APIs (search, recommend, detail).
  • src/services/clubService.ts
    • Added new service for interacting with club-related APIs (my clubs, check name, create, recommendations, search, join).
  • src/services/imageService.ts
    • Added new service for handling image uploads to S3 via presigned URLs.
  • src/services/memberService.ts
    • Added new service for interacting with member-related APIs (recommended members).
  • src/services/storyService.ts
    • Added new service for interacting with book story and comment APIs (get all, get by ID, create story, create/update/delete comment).
  • src/types/book.ts
    • Added new type definitions for Book and BookSearchResponse.
  • src/types/groups/bookcasedetail.ts
    • Added new type definitions for meeting teams and members, including TeamMember, GetMeetingTeamsResult, and TeamMemberListPutBody.
  • src/types/groups/bookcasehome.ts
    • Added new type definitions for bookcase API responses and UI models, including utility functions for mapping and grouping.
  • src/types/groups/clubCreate.ts
    • Added new type definitions for club creation requests and category mapping utilities.
  • src/types/groups/clubsearch.ts
    • Added new type definitions for club search parameters, results, recommendations, and join requests.
  • src/types/member.ts
    • Added new type definitions for recommended members.
  • src/types/story.ts
    • Added new type definitions for book stories, comments, and related API requests/responses.
  • src/utils/groupMapper.ts
    • Updated import path for Category and ParticipantType.
  • src/utils/time.ts
    • Added new utility function formatTimeAgo for formatting timestamps.
  • src/utils/url.ts
    • Added new utility function isValidUrl for validating URLs.
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.

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 improvements to the application's data fetching and state management by integrating TanStack Query for various data operations, including book searches, story listings, and club management. It also refactors several components to use API-driven data instead of dummy data, enhancing the application's functionality and responsiveness. New features include infinite scrolling for search results and stories, a new club creation wizard, and team management for book club meetings with drag-and-drop functionality. Several utility functions for time formatting, URL validation, and data mapping have been added, contributing to a more robust and maintainable codebase. However, there are a few areas that require attention, particularly regarding code duplication in API client implementations and hardcoded values.

Comment on lines +1 to +105
"use client";

import { useAuthStore } from "@/store/useAuthStore";
import toast from "react-hot-toast";
import { getErrorMessage, ApiError } from "../errors";

interface RequestOptions extends RequestInit {
headers?: Record<string, string>;
params?: Record<string, any>;
timeout?: number; // Timeout in ms (default: 10000)
}

async function request<T>(
url: string,
options: RequestOptions = {}
): Promise<T> {
const { params, timeout = 10000, ...fetchOptions } = options;

const defaultHeaders: Record<string, string> = {
"Content-Type": "application/json",
};

// [Utility] Query String Builder
let requestUrl = url;
if (params) {
const searchParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
searchParams.append(key, String(value));
}
});
requestUrl += `?${searchParams.toString()}`;
}

// [Resilience] Timeout Controller
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);

const config: RequestInit = {
...fetchOptions,
// [Security] Include credentials (cookies) for all requests
credentials: "include",
headers: {
...defaultHeaders,
...options.headers,
},
signal: controller.signal,
};

try {
const response = await fetch(requestUrl, config);
clearTimeout(id);

// [Resilience] Interceptor: 401 Unauthorized Handling
if (response.status === 401) {
console.warn("Session expired. Logging out...");
useAuthStore.getState().logout();
toast.error("세션이 만료되었습니다. 다시 로그인해주세요.");
}

// [Resilience] Safe JSON Parsing
let data: any;
const contentType = response.headers.get("content-type");
if (contentType && contentType.includes("application/json")) {
data = await response.json();
} else {
data = {
isSuccess: false,
message: "서버 응답 형식이 올바르지 않습니다.",
};
}

// [Standardization] Response Normalization
if (!response.ok || (data && data.isSuccess === false)) {
const errorCode = data?.code || `HTTP${response.status}`;
const errorMessage =
data?.message ||
getErrorMessage(errorCode) ||
"요청 처리 중 오류가 발생했습니다.";

throw new ApiError(errorMessage, errorCode, data);
}

return data;
} catch (error) {
clearTimeout(id);
console.error("API Request Error:", error);
if (error instanceof DOMException && error.name === "AbortError") {
toast.error("요청 시간이 초과되었습니다.");
throw new Error("Request timeout");
}
throw error;
}
}

export const apiClient = {
get: <T>(url: string, options?: RequestOptions) =>
request<T>(url, { ...options, method: "GET" }),
post: <T>(url: string, body?: any, options?: RequestOptions) =>
request<T>(url, { ...options, method: "POST", body: body ? JSON.stringify(body) : undefined }),
put: <T>(url: string, body?: any, options?: RequestOptions) =>
request<T>(url, { ...options, method: "PUT", body: body ? JSON.stringify(body) : undefined }),
delete: <T>(url: string, options?: RequestOptions) =>
request<T>(url, { ...options, method: "DELETE" }),
};
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

There are two nearly identical API client implementations: src/lib/api/client.ts and src/lib/api/client/index.ts. This code duplication is a critical issue as it leads to inconsistencies (e.g., src/lib/api/client/index.ts is missing the patch method) and makes maintenance significantly harder. These files should be merged into a single, canonical API client implementation.

subscribingCount={u.subscribingCount}
subscribersCount={u.subscribersCount}
onSubscribeClick={() => console.log("subscribe", u.id)}
key={u.nickname + idx}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Using u.nickname + idx as a key can lead to issues if nicknames are not guaranteed to be unique and the list order changes. It's generally safer to use a unique identifier from the user object if available, or a more robust method to generate unique keys if nickname can be duplicated and idx is not stable across renders.

imageUrl={isValidUrl(story.bookInfo.imgUrl) ? story.bookInfo.imgUrl : "/book_example.svg"}
authorName={story.authorInfo.nickname}
authorNickname={story.authorInfo.nickname}
authorId={story.authorInfo.nickname}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The authorId prop in BookstoryDetail is currently being passed story.authorInfo.nickname. If authorId is intended to be a unique identifier for the author (e.g., a numeric ID from the backend), using a nickname might lead to unexpected behavior or incorrect data associations if nicknames are not guaranteed to be unique. Please confirm if authorId is meant for display or for internal logic requiring a unique ID.


const handleAddTeam = () => {
setTeams((prev) => {
if (prev.length >= 7) return prev;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The maximum number of teams is hardcoded to 7. It would be better to define this as a named constant (e.g., MAX_TEAMS) for improved readability and easier modification in the future. This constant is already defined in src/types/groups/bookcasedetail.ts as MAX_TEAMS.

Suggested change
if (prev.length >= 7) return prev;
if (prev.length >= MAX_TEAMS) return prev;

<ButtonWithoutImg
text="이번 모임 바로가기"
onClick={() => router.push(joinUrl)}
onClick={() => router.push(`${Number(groupId)}/notice/4`)}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Hardcoding the notice ID 4 makes the application less flexible and harder to maintain. This ID should ideally be fetched dynamically, perhaps representing the latest or most relevant meeting notice, rather than a fixed value.

Comment on lines 175 to +182
className={[
'h-[28px] t:h-[40px] px-4 py-2 w-full',
'flex items-center justify-center',
'rounded-[8px]',
'bg-primary-2 text-White hover:brightness-90 cursor-pointer',
'body_2_2 t:body_1_2',
'mb-1',
].join(' ')}
"h-[28px] t:h-[40px] px-4 py-2 w-full",
"flex items-center justify-center",
"rounded-[8px]",
"bg-primary-2 text-White",
"body_2_2 t:body_1_2",
"mb-1",
].join(" ")}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The hover effects (hover:brightness-90 cursor-pointer) were removed from this button. Reintroducing visual feedback for interactive elements improves user experience.

Suggested change
className={[
'h-[28px] t:h-[40px] px-4 py-2 w-full',
'flex items-center justify-center',
'rounded-[8px]',
'bg-primary-2 text-White hover:brightness-90 cursor-pointer',
'body_2_2 t:body_1_2',
'mb-1',
].join(' ')}
"h-[28px] t:h-[40px] px-4 py-2 w-full",
"flex items-center justify-center",
"rounded-[8px]",
"bg-primary-2 text-White",
"body_2_2 t:body_1_2",
"mb-1",
].join(" ")}
className={[
"h-[28px] t:h-[40px] px-4 py-2 w-full",
"flex items-center justify-center",
"rounded-[8px]",
"bg-primary-2 text-White hover:brightness-90 cursor-pointer",
"body_2_2 t:body_1_2",
"mb-1",
].join(" ")}

Comment on lines +193 to +199
"h-[28px] t:h-[40px] px-4 py-2 w-full",
"flex items-center justify-center gap-[10px]",
"rounded-[8px]",
"border border-primary-1",
"bg-background text-primary-3",
"body_2_2 t:body_1_2",
].join(" ")}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The hover effects (hover:brightness-95 cursor-pointer) were removed from this button. Reintroducing visual feedback for interactive elements improves user experience.

              className={[
                "h-[28px] t:h-[40px] px-4 py-2 w-full",
                "flex items-center justify-center gap-[10px]",
                "rounded-[8px]",
                "border border-primary-1",
                "bg-background text-primary-3 hover:brightness-95 cursor-pointer",
                "body_2_2 t:body_1_2",
              ].join(" ")}

Comment on lines +221 to +223
"mt-4 w-full h-[44px] rounded-[10px] body_1_2",
reason.trim() ? "bg-primary-2 hover:bg-primary-1 text-White" : "bg-Gray-2 text-Gray-4",
].join(" ")}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The hover effects (hover:brightness-95 cursor-pointer) were removed from this button. Reintroducing visual feedback for interactive elements improves user experience.

              reason.trim() ? "bg-primary-2 hover:bg-primary-1 text-White" : "bg-Gray-2 text-Gray-4",
            ].join(" ")}

onTopicClick={() => { } }
onReviewClick={() => { } }
onMeetingClick={() => { } }
imageUrl={''} />
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The BookcaseCard component expects a string for imageUrl. Passing an empty string ('') is semantically an invalid URL and might lead to broken image icons or unexpected behavior if the component doesn't handle empty strings gracefully. Consider passing a valid placeholder image URL or null/undefined if the prop is optional and the component has a robust fallback.

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.

[feat] 모임 생성/검색(추천)/가입시청 API 연결

2 participants