-
Notifications
You must be signed in to change notification settings - Fork 2
✨feat: 대시보드 수정 페이지 API 연동 #100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
""" Walkthrough이 변경사항은 대시보드 및 협업 관련 컴포넌트의 상태 관리와 데이터 흐름을 리팩토링하고, 여러 기능을 커스텀 훅과 재사용 가능한 컴포넌트로 분리하였습니다. 또한, 정적 목업 데이터를 동적 API 기반 데이터로 대체하고, 토스트 알림 및 스타일링 일부를 수정하였습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant EditInvitation
participant ReactQuery
participant API
User->>EditInvitation: 페이지 진입 및 초대 취소 요청
EditInvitation->>ReactQuery: invitations 데이터 fetch 및 mutate 요청
ReactQuery->>API: GET/DELETE /invitations API 호출
API-->>ReactQuery: 응답 반환 (초대 목록 또는 삭제 결과)
ReactQuery-->>EditInvitation: 상태 및 데이터 업데이트
EditInvitation-->>User: UI 갱신 및 토스트 알림 표시
sequenceDiagram
participant User
participant DashboardForm
participant useDashboardForm
participant API
participant ReactQuery
participant Router
User->>DashboardForm: 폼 입력 및 제출
DashboardForm->>useDashboardForm: onChange, onSubmit 이벤트 전달
useDashboardForm->>API: POST/PUT /dashboard API 호출
API-->>useDashboardForm: 성공/실패 응답
useDashboardForm->>ReactQuery: 캐시 무효화 요청
useDashboardForm->>Router: 페이지 이동 및 새로고침
useDashboardForm-->>DashboardForm: 상태 및 에러 전달
DashboardForm-->>User: UI 갱신 및 알림 표시
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
""" Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
npm error Exit handler never called! 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
⏰ Context from checks skipped due to timeout of 90000ms (1)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🔭 Outside diff range comments (1)
src/app/shared/components/common/header/NavItem.tsx (1)
13-13: 사용되지 않는activeprop을 타입 정의에서 제거 필요컴포넌트 로직에서
activeprop이 더 이상 사용되지 않으므로 타입 정의에서도 제거해야 합니다.type NavItemProps = { as?: 'link' | 'button' href?: string onClick?: () => void iconSrc: string label: string - active?: boolean className?: string }
🧹 Nitpick comments (8)
src/app/features/dashboard/components/edit/PaginationHeader.tsx (2)
31-42: 이전 버튼의 접근성과 상태 처리를 개선해주세요.버튼의 disabled 상태 처리는 올바르지만, 접근성을 위해 aria-disabled 속성과 시각적 피드백을 개선할 수 있습니다.
- <button onClick={onPrev} disabled={currentPage === 1}> + <button + onClick={onPrev} + disabled={currentPage === 1} + aria-disabled={currentPage === 1} + className={currentPage === 1 ? 'cursor-not-allowed opacity-50' : 'hover:opacity-80'} + >
43-54: 다음 버튼도 동일한 접근성 개선이 필요합니다.이전 버튼과 마찬가지로 접근성과 시각적 피드백을 개선해주세요.
- <button onClick={onNext} disabled={currentPage === totalPages}> + <button + onClick={onNext} + disabled={currentPage === totalPages} + aria-disabled={currentPage === totalPages} + className={currentPage === totalPages ? 'cursor-not-allowed opacity-50' : 'hover:opacity-80'} + >src/app/shared/components/dashboard/DashboardForm.tsx (1)
85-96: 폼 제출 버튼의 비활성화 로직을 개선해주세요.현재 로직은 올바르지만, 코드 가독성을 위해 조건을 별도 변수로 분리하는 것을 제안합니다.
+ const isFormInvalid = !formData.title || !formData.color || isSubmitting + <button type="submit" - disabled={!formData.title || !formData.color || isSubmitting} - className={`BG-violet h-48 ${submitButtonWidth} rounded-8 px-16 py-10 text-16 font-semibold text-white transition-opacity ${ - !formData.title || !formData.color || isSubmitting - ? 'cursor-not-allowed opacity-50' - : 'hover:opacity-90' - }`} + disabled={isFormInvalid} + className={`BG-violet h-48 ${submitButtonWidth} rounded-8 px-16 py-10 text-16 font-semibold text-white transition-opacity ${ + isFormInvalid + ? 'cursor-not-allowed opacity-50' + : 'hover:opacity-90' + }`} >src/app/shared/components/common/UserInfo.tsx (1)
19-19: 빈 문자열 체크 로직을 강화해보세요.현재 빈 문자열만 체크하고 있는데, 공백만 있는 문자열도 고려해볼 수 있습니다.
- if (!displayNickname) return null // 사용자 정보가 없는 경우 렌더링하지 않음 + if (!displayNickname || !displayNickname.trim()) return null // 사용자 정보가 없거나 공백만 있는 경우 렌더링하지 않음src/app/features/dashboard/components/edit/DeleteDashboardButton.tsx (1)
35-35: 사용자 경험을 위해 alert 대신 toast 알림 사용을 고려해보세요.현재 alert을 사용하고 있는데, 다른 컴포넌트에서 사용하는 toast 알림과 일관성을 위해 변경을 제안합니다.
+import { showError } from '@/app/shared/lib/toast' // onError 콜백에서 - alert(message) + showError(message) - alert('알 수 없는 오류가 발생했습니다.') + showError('알 수 없는 오류가 발생했습니다.')Also applies to: 39-39
src/app/features/dashboard/components/edit/EditMember.tsx (1)
99-99: 불필요한 nullish coalescing 연산자 사용
profileImageUrl이 null/undefined일 때 빈 문자열로 대체하고 있지만,UserInfo컴포넌트가 null 값을 처리할 수 있다면 이는 불필요할 수 있습니다.-imageUrl={member.profileImageUrl ?? ''} +imageUrl={member.profileImageUrl}src/app/shared/components/common/header/Collaborator/CollaboratorList.tsx (2)
51-51: 툴팁 내용이 혼란스러울 수 있습니다.추가 인원 표시의 툴팁에서 첫 번째 협업자의 이름을 사용하는 것은 직관적이지 않습니다. 전체 협업자 목록이나 더 명확한 메시지를 표시하는 것이 좋습니다.
-content={`${visibleCollaborators[0].nickname} 외 ${extraCount}명`} +content={`${extraCount}명의 추가 협업자`}
31-33: 주석이 코드와 일치하지 않습니다.주석은 "프로필 이미지 및 닉네임만 필요한 경우"라고 되어 있지만, 실제로는 전체 멤버 객체를 슬라이싱하고 있습니다. 주석을 수정하거나 제거하세요.
-// 프로필 이미지 및 닉네임만 필요한 경우 +// 표시할 협업자 목록 (최대 4명)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
public/images/management.pngis excluded by!**/*.png
📒 Files selected for processing (20)
src/app/dashboard/[id]/edit/page.tsx(1 hunks)src/app/features/dashboard/components/edit/DeleteDashboardButton.tsx(2 hunks)src/app/features/dashboard/components/edit/EditInfo.tsx(1 hunks)src/app/features/dashboard/components/edit/EditInvitation.tsx(1 hunks)src/app/features/dashboard/components/edit/EditMember.tsx(1 hunks)src/app/features/dashboard/components/edit/PaginationHeader.tsx(1 hunks)src/app/shared/components/common/Avatar.tsx(2 hunks)src/app/shared/components/common/CollaboratorItem.tsx(1 hunks)src/app/shared/components/common/UserInfo.tsx(1 hunks)src/app/shared/components/common/header/Collaborator/CollaboratorList.tsx(1 hunks)src/app/shared/components/common/header/NavItem.tsx(1 hunks)src/app/shared/components/common/header/RightHeaderNav.tsx(1 hunks)src/app/shared/components/common/header/UserDropdown.tsx(1 hunks)src/app/shared/components/common/modal/CreateDashboardModal.tsx(1 hunks)src/app/shared/components/common/modal/CreateInvitationModal.tsx(3 hunks)src/app/shared/components/dashboard/DashboardForm.tsx(1 hunks)src/app/shared/hooks/useDashboardForm.ts(1 hunks)src/app/shared/hooks/useMembers.ts(1 hunks)src/app/shared/hooks/usePagination.ts(1 hunks)src/app/shared/store/useModalStore.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (12)
src/app/shared/components/common/CollaboratorItem.tsx (1)
src/app/shared/components/common/Avatar.tsx (1)
Avatar(31-68)
src/app/shared/components/common/header/UserDropdown.tsx (2)
src/app/shared/components/common/Dropdown/Dropdown.tsx (1)
Dropdown(14-140)src/app/shared/components/common/UserInfo.tsx (1)
UserInfo(13-32)
src/app/shared/components/common/header/RightHeaderNav.tsx (3)
src/app/shared/store/useModalStore.ts (1)
useModalStore(6-10)src/app/shared/store/useSelectedDashboardStore.ts (1)
useSelectedDashboardStore(10-15)src/app/shared/components/common/header/NavItem.tsx (1)
NavItem(17-57)
src/app/shared/hooks/useDashboardForm.ts (3)
src/app/shared/store/useSelectedDashboardStore.ts (1)
useSelectedDashboardStore(10-15)src/app/shared/types/dashboard.ts (1)
CreateDashboardRequest(31-34)src/app/shared/constants/colors.ts (1)
DASHBOARD_COLORS(3-9)
src/app/shared/components/common/UserInfo.tsx (2)
src/app/features/auth/store/useAuthStore.ts (1)
useAuthStore(6-21)src/app/shared/components/common/Avatar.tsx (1)
Avatar(31-68)
src/app/shared/components/common/Avatar.tsx (1)
src/app/shared/lib/getColor.ts (1)
getColor(1-5)
src/app/shared/components/common/modal/CreateInvitationModal.tsx (2)
src/app/features/dashboard/api/invitation.ts (1)
inviteUser(9-17)src/app/shared/lib/toast.ts (2)
showSuccess(3-3)showError(4-4)
src/app/shared/components/common/header/Collaborator/CollaboratorList.tsx (2)
src/app/shared/components/common/header/Collaborator/Tooltip.tsx (1)
Tooltip(12-70)src/app/shared/components/common/CollaboratorItem.tsx (1)
CollaboratorItem(15-27)
src/app/features/dashboard/components/edit/EditMember.tsx (4)
src/app/shared/lib/toast.ts (2)
showSuccess(3-3)showError(4-4)src/app/features/dashboard/components/edit/PaginationHeader.tsx (1)
PaginationHeader(15-59)src/app/shared/lib/cn.ts (1)
cn(4-6)src/app/shared/components/common/UserInfo.tsx (1)
UserInfo(13-32)
src/app/shared/components/common/header/NavItem.tsx (1)
src/app/shared/lib/cn.ts (1)
cn(4-6)
src/app/shared/components/dashboard/DashboardForm.tsx (2)
src/app/features/dashboard_Id/Card/cardFormModals/input/Input.tsx (1)
Input(9-28)src/app/shared/constants/colors.ts (1)
DASHBOARD_COLORS(3-9)
src/app/features/dashboard/components/edit/EditInfo.tsx (2)
src/app/shared/hooks/useDashboardForm.ts (1)
useDashboardForm(14-111)src/app/shared/components/dashboard/DashboardForm.tsx (1)
DashboardForm(22-99)
🔇 Additional comments (32)
src/app/shared/store/useModalStore.ts (1)
7-9: 타입 안정성 개선을 위한 좋은 변경사항입니다.
null대신 문자열 센티널 값'none'을 사용하여 타입 시스템의 일관성을 향상시켰습니다. 이는 null 체크를 피하고 더 명확한 상태 관리를 가능하게 합니다.src/app/shared/hooks/useMembers.ts (1)
10-10:isOwner타입을 명확히 해주세요.
isOwner: boolean | string타입이 혼재되어 있습니다. API 응답에서 문자열로 받아오는 경우가 있는지 확인하고, 가능하다면 boolean으로 통일하는 것이 좋겠습니다.다음 스크립트로 API 응답 형태를 확인해보세요:
#!/bin/bash # isOwner 필드의 사용법을 확인합니다 rg -A 3 -B 3 "isOwner" --type tssrc/app/shared/hooks/usePagination.ts (1)
3-33: 잘 구현된 재사용 가능한 페이지네이션 훅입니다.
- 제네릭 타입 지원으로 유연성 확보
useMemo를 사용한 성능 최적화- 페이지 경계 조건 처리가 적절함
- 깔끔한 API 설계
재사용성이 높은 좋은 구현입니다.
src/app/shared/components/common/Avatar.tsx (1)
31-68: 전역 상태 의존성 제거를 통한 우수한 리팩토링입니다.
- 전역 스토어 의존성 제거로 컴포넌트 재사용성 향상
- Props 기반 설계로 더 예측 가능한 동작
getColor유틸리티 활용으로 일관된 색상 생성- 이미지 로딩 실패 시 적절한 폴백 처리
컴포넌트 아키텍처가 크게 개선되었습니다.
src/app/shared/components/common/CollaboratorItem.tsx (1)
24-24: Avatar 컴포넌트 인터페이스와 일치하는 올바른 prop 이름 변경Avatar 컴포넌트의 업데이트된 인터페이스에 맞춰 prop 이름을
imageUrl에서profileImageUrl로 변경한 것이 적절합니다.src/app/shared/components/common/header/NavItem.tsx (1)
26-31: cn 유틸리티 사용에 대한 유용한 주석 추가외부에서 className을 받을 때 cn 유틸리티 사용을 권장하는 주석이 개발자에게 도움이 됩니다.
src/app/shared/components/common/header/UserDropdown.tsx (3)
14-21: 로그아웃 후 로그인 페이지로 리디렉션하는 개선된 사용자 경험로그아웃 후 메인 페이지(
'/') 대신 로그인 페이지('/login')로 리디렉션하는 것이 더 직관적이고 사용자 친화적입니다.
27-28: 하드코딩된 사용자 정보 제거로 동적 데이터 흐름 개선UserInfo 컴포넌트에서 하드코딩된 nickname prop을 제거하고 자동으로 로그인된 사용자 정보를 불러오도록 변경한 것이 더 유연하고 유지보수하기 좋은 구조입니다.
14-16: 함수 선언 스타일 일관성 확인을 위해--type대신-g옵션으로 확장자를 지정하여 다시 검색해 보겠습니다.#!/bin/bash echo "=== 화살표 함수 패턴 검색 ===" rg -g '*.ts' -g '*.tsx' -A 2 "const [A-Za-z0-9_]\+ *= *\(" . echo -e "\n=== 함수 선언 패턴 검색 ===" rg -g '*.ts' -g '*.tsx' -A 2 "^\s*function [A-Za-z0-9_]\+" .src/app/dashboard/[id]/edit/page.tsx (1)
19-25: 뒤로가기 버튼 스타일링 개선패딩 조정, 이미지 크기 명시, 그리고 일관된 폰트 크기 클래스 추가로 뒤로가기 버튼의 UI가 개선되었습니다.
src/app/shared/components/common/header/RightHeaderNav.tsx (2)
9-9: 불필요한 modalType 상태 추적 제거로 컴포넌트 단순화NavItem에서 active 상태 관리가 제거됨에 따라 modalType 추적도 제거한 것이 일관성 있는 변경입니다.
17-17: 아이콘 파일 형식 변경 및 존재 여부 확인 필요아이콘이 PNG에서 SVG로 변경되었습니다. 새로운 아이콘 파일이 올바른 경로에 존재하는지 확인해 주세요.
#!/bin/bash # Description: 새로운 아이콘 파일 존재 여부 확인 # Expected: config.svg 파일이 public/images/ 경로에 존재해야 함 echo "=== 새로운 아이콘 파일 존재 여부 확인 ===" if [ -f "public/images/config.svg" ]; then echo "✅ config.svg 파일이 존재합니다." ls -la "public/images/config.svg" else echo "❌ config.svg 파일이 존재하지 않습니다." fi echo -e "\n=== 기존 management.png 파일 확인 ===" if [ -f "public/images/management.png" ]; then echo "⚠️ 기존 management.png 파일이 여전히 존재합니다." ls -la "public/images/management.png" else echo "ℹ️ 기존 management.png 파일이 제거되었습니다." fisrc/app/features/dashboard/components/edit/PaginationHeader.tsx (2)
6-13: 타입 정의가 잘 구조화되어 있습니다.컴포넌트의 props 타입이 명확하게 정의되어 있고, optional children prop을 통해 확장성을 제공하는 것이 좋습니다.
28-30: 페이지 표시 순서 확인이 필요합니다.한국어 맥락에서 "총 페이지 중 현재 페이지" 순서가 더 자연스러울 수 있습니다.
현재 표시: "5 페이지 중 1"
제안 표시: "1 / 5 페이지" 또는 "1페이지 (총 5페이지)"UI/UX 팀과 표시 형식을 확인해보시겠어요?
src/app/shared/components/dashboard/DashboardForm.tsx (3)
10-20: props 타입 정의가 잘 구조화되어 있습니다.폼 데이터, 이벤트 핸들러, 옵션들이 명확하게 타입이 정의되어 있고, 기본값과 optional props 처리가 적절합니다.
50-71: 색상 선택 UI가 잘 구현되어 있습니다.DASHBOARD_COLORS 상수를 사용하여 색상 버튼을 렌더링하고, 선택된 색상에 체크마크를 표시하는 로직이 올바릅니다. aria-label을 통한 접근성 고려도 좋습니다.
37-45:Input컴포넌트의 실제 Props 정의를 확인하기 위해 아래 스크립트를 실행해주세요.#!/bin/bash # 1. Input.tsx 상단 200줄 출력 echo "=== src/app/shared/components/Input.tsx (1~200줄) ===" sed -n '1,200p' src/app/shared/components/Input.tsx || true # 2. Props 인터페이스/타입 검색 echo -e "\n=== Props 정의 검색 ===" rg -n "type InputProps" src/app/shared/components/Input.tsx || true rg -n "interface InputProps" src/app/shared/components/Input.tsx || truesrc/app/shared/components/common/UserInfo.tsx (3)
8-8: nickname을 optional로 만든 변경이 좋습니다.이전 필수 prop에서 optional로 변경하여 컴포넌트의 재사용성이 향상되었습니다.
16-19: fallback 로직과 early return 처리가 잘 구현되어 있습니다.props와 사용자 스토어 데이터 간의 우선순위가 명확하고, 표시할 정보가 없을 때의 early return 처리가 적절합니다.
24-28: Avatar 컴포넌트에 props 전달이 올바릅니다.relevant code snippets에서 확인한 Avatar 컴포넌트의 새로운 인터페이스에 맞게
nickname과profileImageUrl을 정확히 전달하고 있습니다.src/app/features/dashboard/components/edit/DeleteDashboardButton.tsx (4)
18-26: React Query mutation 설정이 잘 구현되어 있습니다.환경변수 검증과 API 호출 로직이 올바르게 mutationFn에 구현되어 있습니다.
27-42: 성공/에러 핸들링이 체계적으로 구현되어 있습니다.onSuccess에서 라우터 리다이렉션, onError에서 axios 에러 타입 체크와 사용자 피드백이 잘 처리되어 있습니다.
44-50: 사용자 확인 처리가 적절합니다.삭제 전 확인 다이얼로그와 조건부 실행 로직이 올바르게 구현되어 있습니다.
56-56: isPending 사용으로 정확히 수정되었습니다.React Query v4+에서 isLoading이 isPending으로 변경된 것을 올바르게 반영했습니다.
src/app/shared/components/common/modal/CreateInvitationModal.tsx (5)
5-5: React Query 클라이언트 추가가 적절합니다.캐시 관리를 위한 useQueryClient 훅 사용이 올바르게 추가되었습니다.
Also applies to: 17-17
36-40: 캐시 무효화 전략이 잘 구현되어 있습니다.초대 성공 후 해당 대시보드의 초대 내역을 새로고침하는 캐시 무효화가 적절히 구현되어 있습니다.
19-23: 인라인 함수를 명명 함수로 리팩토링한 것이 좋습니다.handleBackdropClick과 handleSubmit 함수로 분리하여 코드 가독성이 향상되었습니다.
Also applies to: 25-47
49-49: 모달 조건부 렌더링이 명확해졌습니다.modalType 체크 로직이 명시적으로 변경되어 코드 의도가 더 명확해졌습니다.
37-39: 쿼리 키 검색 시 파일 타입 인식 오류가 발생한 것으로 보입니다. ts와 tsx 파일을 모두 포함하도록 glob 패턴을 사용해 재검증해보세요.#!/bin/bash # ts, tsx 파일에서 invitations 쿼리 키 패턴 검색 rg -A 3 -B 3 "queryKey.*invitations" -g '*.ts' -g '*.tsx'src/app/shared/components/common/modal/CreateDashboardModal.tsx (1)
1-61: 깔끔한 리팩토링입니다!커스텀 훅(
useDashboardForm)과 재사용 가능한 컴포넌트(DashboardForm)를 활용하여 관심사를 잘 분리했습니다. 모달이 닫힐 때 폼을 초기화하는 처리도 적절합니다.src/app/features/dashboard/components/edit/EditInvitation.tsx (1)
122-159: 초대 목록 UI 구현이 잘 되어있습니다.빈 상태 처리, 툴팁을 통한 닉네임 표시, 취소 버튼의 로딩 상태 등이 적절히 구현되었습니다.
src/app/features/dashboard/components/edit/EditInfo.tsx (1)
1-37: 완벽한 컴포넌트 리팩토링입니다!
useDashboardForm훅과DashboardForm컴포넌트를 활용하여 로직과 UI를 완전히 분리했습니다. 이는 재사용성과 유지보수성을 크게 향상시킵니다.
src/app/shared/hooks/useMembers.ts
Outdated
| const teamId = process.env.NEXT_PUBLIC_TEAM_ID | ||
|
|
||
| export async function fetchMembers(dashboardId: string): Promise<Member[]> { | ||
| const { data } = await authHttpClient.get(`/${teamId}/members`, { | ||
| params: { | ||
| page: 1, | ||
| size: 100, | ||
| dashboardId, | ||
| }, | ||
| }) | ||
| return data.members ?? data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
환경 변수 에러 처리 및 데이터 구조 명확화가 필요합니다.
teamId가undefined일 수 있는 경우에 대한 에러 처리가 필요합니다.data.members ?? data폴백 로직의 의도를 명확히 해주세요.
다음과 같이 개선할 수 있습니다:
+const teamId = process.env.NEXT_PUBLIC_TEAM_ID
+if (!teamId) {
+ throw new Error('NEXT_PUBLIC_TEAM_ID 환경변수가 설정되지 않았습니다.')
+}
export async function fetchMembers(dashboardId: string): Promise<Member[]> {
const { data } = await authHttpClient.get(`/${teamId}/members`, {
params: {
page: 1,
size: 100,
dashboardId,
},
})
+ // API 응답 구조에 따라 members 배열 또는 전체 데이터 반환
return data.members ?? data
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/app/shared/hooks/useMembers.ts around lines 14 to 24, add a check to
throw an explicit error if the environment variable teamId is undefined before
making the API call. Also, clarify the fallback logic by explicitly checking if
data.members exists and returning it; otherwise, handle or throw an error if the
data structure is unexpected, instead of using a fallback that may mask issues.
| const message = axios.isAxiosError(error) | ||
| ? error.response?.data?.message || | ||
| '대시보드 요청 중 오류가 발생했습니다.' | ||
| : '알 수 없는 오류가 발생했습니다.' | ||
| alert(message) | ||
| console.error('대시보드 오류:', error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
사용자 경험을 위해 에러 처리 방식을 개선해주세요.
alert()를 사용한 에러 표시는 사용자 경험에 좋지 않습니다. 토스트 알림이나 인라인 에러 메시지를 사용하는 것을 고려해보세요.
에러 상태를 훅에서 관리하고 컴포넌트에서 적절히 표시하도록 개선할 수 있습니다:
+const [error, setError] = useState<string | null>(null)
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
// ...
try {
+ setError(null)
setIsSubmitting(true)
// ... API 호출
} catch (error) {
const message = axios.isAxiosError(error)
? error.response?.data?.message ||
'대시보드 요청 중 오류가 발생했습니다.'
: '알 수 없는 오류가 발생했습니다.'
- alert(message)
+ setError(message)
console.error('대시보드 오류:', error)
}
}
return {
// ... 다른 값들
+ error,
+ clearError: () => setError(null),
}📝 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 message = axios.isAxiosError(error) | |
| ? error.response?.data?.message || | |
| '대시보드 요청 중 오류가 발생했습니다.' | |
| : '알 수 없는 오류가 발생했습니다.' | |
| alert(message) | |
| console.error('대시보드 오류:', error) | |
| // ─────────────────────────────────────────────────────────────────────────────── | |
| // at the top of your hook (above handleSubmit) | |
| const [error, setError] = useState<string | null>(null) | |
| const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { | |
| e.preventDefault() | |
| + setError(null) | |
| setIsSubmitting(true) | |
| try { | |
| // ... API 호출 | |
| } catch (error) { | |
| const message = axios.isAxiosError(error) | |
| ? error.response?.data?.message || | |
| '대시보드 요청 중 오류가 발생했습니다.' | |
| : '알 수 없는 오류가 발생했습니다.' | |
| - alert(message) | |
| + setError(message) | |
| console.error('대시보드 오류:', error) | |
| } finally { | |
| setIsSubmitting(false) | |
| } | |
| } | |
| return { | |
| // ... 다른 값들 | |
| error, | |
| clearError: () => setError(null), | |
| } |
🤖 Prompt for AI Agents
In src/app/shared/hooks/useDashboardForm.ts around lines 91 to 96, replace the
alert-based error notification with state management for the error message
inside the hook. Create a state variable to hold the error message, set it when
an error occurs, and return this state from the hook so the component can
display the error using a toast notification or inline message instead of
alert().
|
|
||
| import { mockMembers } from './mockMember' | ||
| const PAGE_SIZE = 4 | ||
| const teamId = process.env.NEXT_PUBLIC_TEAM_ID |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
환경 변수가 정의되지 않았을 때를 대비한 처리가 필요합니다.
process.env.NEXT_PUBLIC_TEAM_ID가 undefined일 경우를 처리해야 합니다.
-const teamId = process.env.NEXT_PUBLIC_TEAM_ID
+const teamId = process.env.NEXT_PUBLIC_TEAM_ID
+
+if (!teamId) {
+ throw new Error('NEXT_PUBLIC_TEAM_ID가 환경 변수에 설정되지 않았습니다.')
+}📝 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 teamId = process.env.NEXT_PUBLIC_TEAM_ID | |
| const teamId = process.env.NEXT_PUBLIC_TEAM_ID | |
| if (!teamId) { | |
| throw new Error('NEXT_PUBLIC_TEAM_ID가 환경 변수에 설정되지 않았습니다.') | |
| } |
🤖 Prompt for AI Agents
In src/app/features/dashboard/components/edit/EditMember.tsx at line 16, the
code assigns process.env.NEXT_PUBLIC_TEAM_ID directly to teamId without handling
the case when this environment variable is undefined. Add a check to verify if
process.env.NEXT_PUBLIC_TEAM_ID is defined; if not, handle the situation
appropriately, such as setting a default value or throwing a clear error to
prevent runtime issues.
| // ✅ dashboardId 안전 처리 | ||
| const rawDashboardId = params.id | ||
| const dashboardId: string = | ||
| typeof rawDashboardId === 'string' | ||
| ? rawDashboardId | ||
| : (rawDashboardId?.[0] ?? '') | ||
|
|
||
| // ✅ teamId는 환경변수에서 가져오며 string으로 강제 처리 | ||
| const rawTeamId = process.env.NEXT_PUBLIC_TEAM_ID | ||
| const teamId: string = | ||
| typeof rawTeamId === 'string' ? rawTeamId : (rawTeamId?.[0] ?? '') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
파라미터 처리가 지나치게 복잡합니다.
useParams의 반환값과 환경 변수 처리가 과도하게 방어적입니다. Next.js의 useParams는 일반적으로 문자열을 반환하므로 이러한 복잡한 처리는 불필요할 수 있습니다.
-// ✅ dashboardId 안전 처리
-const rawDashboardId = params.id
-const dashboardId: string =
- typeof rawDashboardId === 'string'
- ? rawDashboardId
- : (rawDashboardId?.[0] ?? '')
-
-// ✅ teamId는 환경변수에서 가져오며 string으로 강제 처리
-const rawTeamId = process.env.NEXT_PUBLIC_TEAM_ID
-const teamId: string =
- typeof rawTeamId === 'string' ? rawTeamId : (rawTeamId?.[0] ?? '')
+const dashboardId = params.id as string
+const teamId = process.env.NEXT_PUBLIC_TEAM_ID as string
+
+if (!teamId) {
+ throw new Error('NEXT_PUBLIC_TEAM_ID가 설정되지 않았습니다.')
+}📝 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.
| // ✅ dashboardId 안전 처리 | |
| const rawDashboardId = params.id | |
| const dashboardId: string = | |
| typeof rawDashboardId === 'string' | |
| ? rawDashboardId | |
| : (rawDashboardId?.[0] ?? '') | |
| // ✅ teamId는 환경변수에서 가져오며 string으로 강제 처리 | |
| const rawTeamId = process.env.NEXT_PUBLIC_TEAM_ID | |
| const teamId: string = | |
| typeof rawTeamId === 'string' ? rawTeamId : (rawTeamId?.[0] ?? '') | |
| const dashboardId = params.id as string | |
| const teamId = process.env.NEXT_PUBLIC_TEAM_ID as string | |
| if (!teamId) { | |
| throw new Error('NEXT_PUBLIC_TEAM_ID가 설정되지 않았습니다.') | |
| } |
🤖 Prompt for AI Agents
In src/app/features/dashboard/components/edit/EditInvitation.tsx around lines 30
to 40, the code overcomplicates extracting dashboardId and teamId by defensively
handling types that are unlikely given Next.js guarantees. Simplify by directly
assigning params.id and process.env.NEXT_PUBLIC_TEAM_ID to dashboardId and
teamId as strings without conditional checks, assuming they are strings as
expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/app/features/dashboard/components/edit/DeleteDashboardButton.tsx (1)
20-43: React Query mutation으로의 리팩토링이 우수합니다.블로킹 대화상자에서 토스트 기반 확인으로 변경하여 UX를 개선하고, React Query의 mutation lifecycle을 잘 활용한 점이 좋습니다.
환경변수 접근의 일관성을 위해 getTeamId 함수 사용을 권장합니다.
22-24줄에서 직접
process.env.NEXT_PUBLIC_TEAM_ID를 확인하는 대신, 새로 도입된getTeamId유틸리티 함수를 사용하면 더 일관된 코드가 됩니다.+import { getTeamId } from '@/app/shared/lib/getTeamId' const mutation = useMutation<void, Error, void>({ mutationFn: async () => { - if (!process.env.NEXT_PUBLIC_TEAM_ID) { - throw new Error('NEXT_PUBLIC_TEAM_ID 환경변수가 설정되지 않았습니다.') - } + const teamId = getTeamId() await api.delete( - `/${process.env.NEXT_PUBLIC_TEAM_ID}/dashboards/${dashboardId}`, + `/${teamId}/dashboards/${dashboardId}`, ) },src/app/features/dashboard/components/edit/EditInvitation.tsx (1)
89-97: 에러 메시지 처리 로직을 단순화할 수 있습니다.현재 에러 처리 로직이 불필요하게 복잡합니다. 더 간단하고 읽기 쉽게 개선할 수 있습니다.
-// 에러 메시지 정리 -const errorMessage = - isError && axios.isAxiosError(error) - ? error.response?.status === 403 - ? '초대 권한이 없습니다.' - : '초대 정보를 불러오는 데 실패했습니다.' - : isError - ? '초대 정보를 불러오는 데 실패했습니다.' - : null +const getErrorMessage = () => { + if (!isError) return null + if (axios.isAxiosError(error) && error.response?.status === 403) { + return '초대 권한이 없습니다.' + } + return '초대 정보를 불러오는 데 실패했습니다.' +} + +const errorMessage = getErrorMessage()
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
src/app/features/dashboard/components/edit/DeleteDashboardButton.tsx(2 hunks)src/app/features/dashboard/components/edit/EditInfo.tsx(1 hunks)src/app/features/dashboard/components/edit/EditInvitation.tsx(1 hunks)src/app/features/dashboard/components/edit/EditMember.tsx(1 hunks)src/app/features/dashboard/components/edit/mockMember.js(0 hunks)src/app/shared/components/common/UserInfo.tsx(1 hunks)src/app/shared/components/common/header/Collaborator/CollaboratorList.tsx(1 hunks)src/app/shared/components/common/header/NavItem.tsx(1 hunks)src/app/shared/components/common/header/RightHeaderNav.tsx(1 hunks)src/app/shared/hooks/useDashboard.ts(1 hunks)src/app/shared/hooks/useDashboardForm.ts(1 hunks)src/app/shared/hooks/useMembers.ts(1 hunks)src/app/shared/lib/getTeamId.ts(1 hunks)
💤 Files with no reviewable changes (1)
- src/app/features/dashboard/components/edit/mockMember.js
✅ Files skipped from review due to trivial changes (1)
- src/app/shared/hooks/useDashboard.ts
🚧 Files skipped from review as they are similar to previous changes (8)
- src/app/shared/components/common/header/NavItem.tsx
- src/app/shared/components/common/header/RightHeaderNav.tsx
- src/app/shared/hooks/useMembers.ts
- src/app/shared/hooks/useDashboardForm.ts
- src/app/shared/components/common/UserInfo.tsx
- src/app/shared/components/common/header/Collaborator/CollaboratorList.tsx
- src/app/features/dashboard/components/edit/EditMember.tsx
- src/app/features/dashboard/components/edit/EditInfo.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/app/features/dashboard/components/edit/DeleteDashboardButton.tsx (1)
src/app/shared/lib/toast.ts (2)
showSuccess(3-3)showError(4-4)
🔇 Additional comments (6)
src/app/shared/lib/getTeamId.ts (1)
1-7: 환경변수 접근을 중앙화한 좋은 접근 방식입니다.
getTeamId함수를 통해 환경변수 접근을 중앙화하고 적절한 에러 처리를 구현한 점이 우수합니다. 이는 코드의 일관성과 유지보수성을 향상시킵니다.src/app/features/dashboard/components/edit/DeleteDashboardButton.tsx (1)
45-54: 토스트 기반 삭제 확인 구현이 훌륭합니다.사용자 경험을 향상시키는 비블로킹 확인 방식이며, 명확한 설명과 액션 버튼을 제공하는 점이 우수합니다.
src/app/features/dashboard/components/edit/EditInvitation.tsx (4)
33-33: 파라미터 처리가 개선되었습니다.과거 리뷰에서 지적된 복잡한 파라미터 처리가 간단하고 명확하게 개선되었습니다.
38-54: React Query를 활용한 데이터 페칭이 우수합니다.적절한 쿼리 키 설정, 에러 재시도 비활성화, enabled 조건 등 React Query 모범 사례를 잘 따르고 있습니다.
67-84: mutation을 통한 초대 취소 기능이 잘 구현되었습니다.적절한 에러 처리, 성공 시 캐시 무효화, 토스트 피드백 등이 잘 구현되어 있습니다.
125-176: 로딩, 에러, 빈 상태 처리가 잘 구현되었습니다.다양한 UI 상태에 대한 적절한 처리와 사용자 피드백이 잘 구현되어 있습니다. Tooltip을 활용한 닉네임 표시와 mutation 상태에 따른 버튼 비활성화도 우수합니다.
| const INVITATION_SIZE = 5 | ||
| const teamId = getTeamId() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
모듈 레벨에서의 getTeamId() 호출을 컴포넌트 내부로 이동하세요.
모듈 레벨에서 getTeamId()를 호출하면 환경변수가 설정되지 않았을 때 모듈 초기화 시점에 에러가 발생할 수 있습니다. 컴포넌트 내부로 이동하는 것이 안전합니다.
-const INVITATION_SIZE = 5
-const teamId = getTeamId()
+const INVITATION_SIZE = 5
export default function EditInvitation() {
const params = useParams()
const { openModal } = useModalStore()
const dashboardId = params.id as string
+ const teamId = getTeamId()
const queryClient = useQueryClient()📝 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 INVITATION_SIZE = 5 | |
| const teamId = getTeamId() | |
| // at top of file, only the constant remains | |
| const INVITATION_SIZE = 5 | |
| export default function EditInvitation() { | |
| const params = useParams() | |
| const { openModal } = useModalStore() | |
| const dashboardId = params.id as string | |
| const teamId = getTeamId() | |
| const queryClient = useQueryClient() | |
| // …rest of the component | |
| } |
🤖 Prompt for AI Agents
In src/app/features/dashboard/components/edit/EditInvitation.tsx at lines 18-19,
move the call to getTeamId() from the module level into the component function
body to avoid errors during module initialization when environment variables are
not set. This means declaring and assigning teamId inside the React component
instead of at the top-level scope.
📌 변경 사항 개요
대시보드 수정 페이지 API 연동
✨ 요약
대시보드 수정 페이지 API 연동
📝 상세 내용
🫧modify:
✨feat:
🎨style:
🐛fix: 사이드바에 대시보드 명 적용안되던 문제 캐시 무효화 및 페이지 강제 갱신으로 해결
🔗 관련 이슈
🖼️ 스크린샷
20250622_081151.mp4
20250622_080834.mp4
✅ 체크리스트
💡 참고 사항
Summary by CodeRabbit
Summary by CodeRabbit
신규 기능
버그 수정
리팩터
스타일
문서화