Conversation
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…r better readability
…breaking with long titles
Feat/128 home api
…/134-re-book-ui
…rCard with variants
[feat] 오늘의 책 추천 UI 수정
[feat] 관리자 로그인 및 기본 관리 페이지 UI 구현
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review infoConfiguration used: defaults Review profile: CHILL Plan: Pro ⛔ Files ignored due to path filters (5)
📒 Files selected for processing (115)
📝 WalkthroughWalkthroughThis pull request introduces a comprehensive API integration layer with React Query for data fetching, adds an admin dashboard with multiple management pages, implements drag-and-drop team management for meetings, and refactors numerous pages to consume real API data instead of mock data. It also adds configuration for remote image loading, environment variable support, and new dependencies for query management and drag-and-drop functionality. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Browser/Client
participant Cache as React Query<br/>Cache
participant API as API Client
participant Server as Backend API
Client->>Cache: useQuery/useInfiniteQuery
activate Cache
Cache->>API: Check cache/needs fetch?
alt Cache Hit & Fresh
Cache-->>Client: Return cached data
else Cache Miss or Stale
Cache->>API: Trigger request
activate API
API->>API: Add headers, handle auth
API->>Server: GET/POST/PATCH/DELETE<br/>(with AbortController timeout)
activate Server
Server-->>API: ApiResponse<T>
deactivate Server
API->>API: Parse JSON, extract result
alt 401 Unauthorized
API->>API: Logout via authStore
API-->>Client: Toast error
else Success
API-->>Cache: Return result
end
deactivate API
Cache->>Cache: Store in cache<br/>(staleTime: 60s)
Cache-->>Client: Update UI with data
end
deactivate Cache
Client->>Client: Render with fetched data
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
✨ 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 |
Summary of ChangesHello @shinwokkang, 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! 이 Pull Request는 애플리케이션의 데이터 관리 방식을 혁신하고, 새로운 관리자 기능을 도입하며, 사용자 경험을 향상시키는 데 중점을 둡니다. 주요 변경 사항은 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
|
There was a problem hiding this comment.
Code Review
이 Pull Request는 애플리케이션 전반에 걸쳐 더미 데이터를 실제 API 연동으로 전환하는 대규모 리팩토링을 포함하고 있습니다. React Query를 도입하여 데이터 페칭 및 캐싱을 관리하고, 관련 서비스와 타입, 커스텀 훅을 체계적으로 구현한 점이 인상적입니다. 또한, 관리자 페이지와 여러 UI 컴포넌트가 추가 및 개선되었습니다. 전반적으로 코드 품질과 아키텍처가 크게 향상되었습니다. 다만, 몇 가지 중요한 버그와 개선점이 발견되어 리뷰 코멘트를 남겼습니다. 특히 관리자 로그인 로직, 깨진 네비게이션 경로, 잘못된 API 설정 토글 등은 배포 전에 반드시 수정해야 합니다.
| const handleLogin = (e: React.FormEvent) => { | ||
| e.preventDefault(); | ||
| if (isDisabled) return; | ||
| setToastMsg("토스트 테스트"); | ||
| }; |
There was a problem hiding this comment.
로그인 핸들러에 실제 로그인 로직 대신 테스트용 토스트 메시지가 하드코딩되어 있습니다. 이대로 배포될 경우 관리자 로그인이 불가능하므로, 실제 API를 호출하는 로그인 로직으로 교체해야 합니다.
| const handleLogin = (e: React.FormEvent) => { | |
| e.preventDefault(); | |
| if (isDisabled) return; | |
| setToastMsg("토스트 테스트"); | |
| }; | |
| const handleLogin = (e: React.FormEvent) => { | |
| e.preventDefault(); | |
| if (isDisabled) return; | |
| // TODO: 실제 로그인 API 호출 로직 구현 | |
| console.log("로그인 시도:", { id, pw }); | |
| }; |
| <ButtonWithoutImg | ||
| text="이번 모임 바로가기" | ||
| onClick={() => router.push(joinUrl)} | ||
| onClick={() => router.push(`${Number(groupId)}/notice/4`)} |
There was a problem hiding this comment.
| const isAuthPage = | ||
| pathname.startsWith("/login") || | ||
| pathname.startsWith("/signup"); |
There was a problem hiding this comment.
인증 페이지에서 헤더를 숨기는 로직이 의도와 다르게 동작할 수 있습니다. 현재 pathname.startsWith("/login")은 루트 경로의 /login에도 해당될 수 있습니다. 이 레이아웃은 /admin 경로 하위에 적용되므로, /admin/login과 같이 전체 경로를 확인하는 것이 더 안전합니다. 그렇지 않으면 예기치 않은 페이지에서 헤더가 사라지는 버그가 발생할 수 있습니다.
| const isAuthPage = | |
| pathname.startsWith("/login") || | |
| pathname.startsWith("/signup"); | |
| const isAuthPage = | |
| pathname === "/admin/login" || | |
| pathname === "/admin/signup"; |
| title="모임 댓글/답글 알림" | ||
| description="작성한 게시글에 대한 댓글 및 답글 알림 수신" | ||
| // There isn't a single clear match for this string in standard setting schema besides what Swagger provided. | ||
| // Currently matching clubMeetingCreated here based on structure, although naming implies 'meeting/gathering'. | ||
| isChecked={settings?.clubMeetingCreated} | ||
| onToggle={() => handleToggle("CLUB_MEETING_CREATED")} | ||
| disabled={isPending} | ||
| /> |
| onTopicClick={() => { } } | ||
| onReviewClick={() => { } } | ||
| onMeetingClick={() => { } } | ||
| imageUrl={''} /> |
| }, [filtered, page, pageSize]); | ||
|
|
||
| const handleSearch = () => { | ||
| console.log("검색:", keyword); |
| <div className="flex flex-col items-start gap-[12px] self-stretch"> | ||
| <label className="self-stretch body_1_2 text-primary-3">이름</label> | ||
| <div className={`${inputContainerClass} self-stretch`}> | ||
| <div className={`${inputContainerClass} self-stretch !bg-Gray-1 !border-Gray-3`}> |
There was a problem hiding this comment.
Tailwind CSS에서 !(important) 한정자는 다른 스타일과의 충돌을 해결하기 위해 사용되지만, 꼭 필요한 경우가 아니라면 남용을 피하는 것이 좋습니다. 스타일 우선순위 관리를 복잡하게 만들 수 있기 때문입니다. disabled 상태 스타일을 적용하기 위해 disabled: variant를 사용하는 것을 고려해볼 수 있습니다.
| <div className={`${inputContainerClass} self-stretch !bg-Gray-1 !border-Gray-3`}> | |
| <div className={`${inputContainerClass} self-stretch bg-Gray-1 border-Gray-3`}> |
| const meetingIdParam = (params.meetingId ?? params.bookId) as string; // 폴더명 차이 커버 | ||
| const meetingId = Number(meetingIdParam); |
| <div | ||
| draggable={draggable} | ||
| onDragStart={(e) => { | ||
| if (!draggable) return; | ||
| e.dataTransfer.setData( | ||
| "application/x-checkmo-member", | ||
| JSON.stringify({ | ||
| clubMemberId: member.clubMemberId, | ||
| fromTeamNumber: member.teamNumber, | ||
| }) | ||
| ); | ||
| e.dataTransfer.effectAllowed = "move"; | ||
| }} | ||
| className={[ | ||
| "flex items-center gap-2.5", | ||
| "w-full self-stretch", | ||
| "px-5 py-4", | ||
| "rounded-[8px]", | ||
| "bg-White", | ||
| draggable ? "cursor-grab active:cursor-grabbing" : "", | ||
| ].join(" ")} |
There was a problem hiding this comment.
이 컴포넌트는 @dnd-kit을 사용하는 부모 컴포넌트 내에서 렌더링됩니다. @dnd-kit은 자체적인 이벤트 리스너와 상태 관리를 통해 드래그 앤 드롭을 구현하므로, 네이티브 HTML5의 draggable 속성과 onDragStart 이벤트 핸들러는 필요하지 않으며 오히려 혼란을 줄 수 있습니다. @dnd-kit의 작동 방식과 일관성을 유지하기 위해 해당 속성들을 제거하는 것이 좋습니다.
| <div | |
| draggable={draggable} | |
| onDragStart={(e) => { | |
| if (!draggable) return; | |
| e.dataTransfer.setData( | |
| "application/x-checkmo-member", | |
| JSON.stringify({ | |
| clubMemberId: member.clubMemberId, | |
| fromTeamNumber: member.teamNumber, | |
| }) | |
| ); | |
| e.dataTransfer.effectAllowed = "move"; | |
| }} | |
| className={[ | |
| "flex items-center gap-2.5", | |
| "w-full self-stretch", | |
| "px-5 py-4", | |
| "rounded-[8px]", | |
| "bg-White", | |
| draggable ? "cursor-grab active:cursor-grabbing" : "", | |
| ].join(" ")} | |
| <div | |
| className={[ | |
| "flex items-center gap-2.5", | |
| "w-full self-stretch", | |
| "px-5 py-4", | |
| "rounded-[8px]", | |
| "bg-White", | |
| draggable ? "cursor-grab active:cursor-grabbing" : "", | |
| ].join(" ")} | |
| > |
📌 개요 (Summary)
🛠️ 변경 사항 (Changes)
📸 스크린샷 (Screenshots)
(UI 변경 사항이 있다면 첨부해주세요)
✅ 체크리스트 (Checklist)
pnpm build)pnpm lint)Summary by CodeRabbit
Release Notes
New Features
Improvements