-
Notifications
You must be signed in to change notification settings - Fork 3
Feat/132/create edit gathering api 연결 #133
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
|
Caution Review failedThe pull request is closed. Walkthrough이 변경 사항은 새로운 모임을 생성하는 기능을 추가하는 것으로, Changes
Possibly related PRs
Suggested labels
Suggested reviewers
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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: 4
🧹 Outside diff range and nitpick comments (19)
src/_queries/gathering/gathering-detail-queries.ts (1)
13-28: 에러 처리를 개선하면 좋을 것 같습니다.구현이 전반적으로 잘 되어 있으나, 에러 메시지를 더 구체적으로 처리하면 좋을 것 같습니다.
다음과 같이 에러 처리를 개선하는 것을 제안드립니다:
- onError: (error) => { - toast.error(error.message); + onError: (error) => { + const errorMessage = error.message || '모임 생성 중 오류가 발생했습니다.'; + toast.error(errorMessage); + console.error('모임 생성 실패:', error); },src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-modal/presenter.tsx (2)
5-11: 인터페이스에 JSDoc 문서화를 추가하면 좋을 것 같습니다.각 프로퍼티의 목적과 사용법을 명확히 이해할 수 있도록 인터페이스에 문서화를 추가하는 것이 좋겠습니다.
다음과 같이 문서화를 추가해보세요:
+/** + * 모임 생성 모달의 프로퍼티를 정의하는 인터페이스 + */ export interface GatheringDetailModalProps { + /** 모달의 표시 여부를 제어 */ opened: boolean; + /** 모달 닫기 핸들러 */ onClose: () => void; + /** 모임 생성 폼 제출 핸들러 */ onSubmit: (data: CreateGatheringFormTypes) => void; + /** 모임 수정 핸들러 */ onEdit: () => void; + /** 모임 데이터 */ data: CreateGatheringFormTypes; }
Line range hint
13-44: 접근성 개선이 필요합니다.모달 컴포넌트의 접근성을 개선하기 위한 몇 가지 제안사항입니다:
aria-label또는aria-labelledby속성 추가- 모달이 열릴 때 포커스 관리
- ESC 키 처리 추가 (현재 Mantine이 처리하고 있을 수 있음)
다음과 같이 수정해보세요:
<Modal opened={opened} onClose={onClose} centered title="약속 만들기" + aria-label="약속 만들기 모달" + trapFocus + returnFocus styles={{ root: { '--modal-size': '520px' },src/app/(crew)/crew/detail/[id]/_components/create-gathering/index.tsx (2)
37-42: 모달 컨테이너에 필요한 props가 올바르게 전달되었습니다.새로 추가된
crewId를 포함하여 모든 필수 props가 적절하게 전달되고 있습니다. 하지만 몇 가지 개선사항을 제안드립니다:
- 초기값 객체를 컴포넌트 외부로 이동하여 불필요한 재생성을 방지
- props 전달 시 가독성을 위해 스프레드 연산자 사용 고려
다음과 같이 수정하는 것을 고려해보세요:
+const INITIAL_GATHERING_VALUE: CreateGatheringFormTypes = { + title: '', + introduce: '', + dateTime: '', + location: '', + totalCount: 2, + imageUrl: null, +}; export default function CreateGathering({ crewId }: { crewId: number }) { const { isAuth } = useAuthStore(); const router = useRouter(); const [opened, { open, close }] = useDisclosure(false); - const initialValue: CreateGatheringFormTypes = { - title: '', - introduce: '', - dateTime: '', - location: '', - totalCount: 2, - imageUrl: null, - }; return ( <> <Button type="button" className="btn-filled px-4" onClick={handleButtonClick}> 약속 만들기 </Button> <CreateGatheringModalContainer - crewId={crewId} - opened={opened} - close={close} - data={initialValue} + {...{ + crewId, + opened, + close, + data: INITIAL_GATHERING_VALUE + }} /> </> ); }
Line range hint
15-22: 인증 상태에 따른 처리가 적절합니다.사용자의 인증 상태에 따라 모달을 열거나 로그인 페이지로 리다이렉트하는 로직이 잘 구현되어 있습니다. 하지만 사용자 경험을 더욱 개선하기 위해 다음 사항을 고려해보세요:
- 로그인 후 원래 페이지로 돌아올 수 있도록 리다이렉트 URL 추가
- 비로그인 상태일 때 사용자에게 안내 메시지 표시
다음과 같이 수정하는 것을 제안드립니다:
const handleButtonClick = () => { if (isAuth) { open(); } else { + // 현재 URL을 state로 전달하여 로그인 후 리다이렉트 + const returnUrl = encodeURIComponent(window.location.pathname); - router.push('/login'); + router.push(`/login?returnUrl=${returnUrl}`); + // TODO: 토스트 메시지 추가 + // showToast('로그인이 필요한 서비스입니다.'); } };src/app/(crew)/crew/detail/[id]/edit/page.tsx (3)
Line range hint
16-31: 초기값 설정 로직 개선 필요현재
initialValue가 하드코딩되어 있으며, API에서 받아온data를 활용하지 않고 있습니다. 수정 기능의 경우 기존 데이터를 폼에 표시해야 합니다.- const initialValue: CreateCrewFormTypes = { - title: '', - mainCategory: '', - subCategory: '', - imageUrl: - 'https://crewcrew.s3.ap-northeast-2.amazonaws.com/crew/0e05d971-15a8-4a32-bf03-80d12cae392e', - mainLocation: '', - subLocation: '', - totalCount: 4, - introduce: '', - }; + const initialValue: CreateCrewFormTypes = { + title: data.title, + mainCategory: data.mainCategory, + subCategory: data.subCategory, + imageUrl: data.imageUrl, + mainLocation: data.mainLocation, + subLocation: data.subLocation, + totalCount: data.totalCount, + introduce: data.introduce, + };
Line range hint
33-34: 미구현된 수정 기능에 대한 처리 필요
handleEdit함수가 구현되지 않았습니다. API가 준비되지 않은 상태라면, 사용자에게 적절한 피드백을 제공해야 합니다.수정 기능 구현이나 임시 처리 로직 작성을 도와드릴까요?
Line range hint
36-53: 접근성 개선 필요이미지에 의미 있는 대체 텍스트가 누락되어 있습니다. 스크린 리더 사용자를 위해 적절한 설명을 추가해야 합니다.
- alt="" + alt="크루 수정 페이지 아이콘"src/app/(crew)/crew/detail/[id]/page.tsx (2)
26-26: Number 변환 중복 개선 필요
params.id를 Number로 변환하는 작업이 중복되어 있습니다. 이미 상단에서 선언된id변수를 재사용하는 것이 좋습니다.다음과 같이 수정하는 것을 제안드립니다:
-<CreateGathering crewId={Number(params.id)} /> +<CreateGathering crewId={id} />
리뷰 섹션 구현이 미완성 상태이며 관련 컴포넌트 준비 필요
리뷰 섹션 관련 파일들이 이미 프로젝트에 존재하지만 실제 구현은 미완성 상태입니다:
review-section.tsx에 TODO 주석과 함께 대부분의 기능이 주석 처리되어 있음- 필요한 컴포넌트들(
RatingDisplay,CrewReviewList)은 이미 준비되어 있으나 실제 데이터 연동 및 구현이 필요한 상태다음 작업들이 필요합니다:
- 리뷰 데이터 연동 구현
RatingDisplay컴포넌트 연동CrewReviewList페이지네이션 구현- 전체 리뷰 섹션 UI 완성
🔗 Analysis chain
Line range hint
31-37: 리뷰 섹션 관련 TODO 처리 계획 필요주석 처리된 리뷰 섹션이 있습니다. 이 기능의 구현 계획과 일정을 명확히 하는 것이 좋을 것 같습니다.
리뷰 섹션 구현을 위한 이슈를 생성하거나 구현 코드를 제안드릴 수 있습니다. 도움이 필요하신가요?
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check if there's an existing issue for the review section implementation gh issue list --search "리뷰 섹션" --state openLength of output: 45
Script:
#!/bin/bash # Check for any TODO comments related to review section implementation rg -i "todo.*리뷰" -g "!*.md" # Check for any CrewReviewSection component references rg "CrewReviewSection" -g "!*.md" # Check for any files with "review" in their name under src directory fd "review" "src"Length of output: 1181
Script:
#!/bin/bash # Check the implementation status of review-section component cat src/app/\(crew\)/crew/detail/\[id\]/_components/review-section.tsx # Check if there are any related PRs rg -l "리뷰" .github/Length of output: 748
src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-modal/container.tsx (3)
9-14: 이미지 URL 타입 개선 제안
CreateGatheringFormTypes에서imageUrl필드가string | File타입을 가질 수 있다는 것을 명시적으로 표현하면 좋을 것 같습니다.export interface CreateGatheringModalContainerProps { crewId: number; opened: boolean; close: () => void; - data: CreateGatheringFormTypes; + data: Omit<CreateGatheringFormTypes, 'imageUrl'> & { + imageUrl: string | File; + }; }
42-45: TODO 항목 추적 필요약속 수정 API가 준비되면 구현이 필요한 부분입니다.
이 TODO 항목을 GitHub 이슈로 생성하여 추적하시겠습니까?
47-63: 로딩 UI 개선 제안현재 로딩 상태 UI가 너무 단순합니다. 사용자 경험 향상을 위해 다음과 같은 개선을 제안합니다:
if (isPending) return ( - <div className="fixed inset-0 z-10 flex items-center justify-center"> - <Loader size="sm" /> + <div className="fixed inset-0 z-10 flex items-center justify-center bg-black/50"> + <div className="bg-white p-4 rounded-lg shadow-lg"> + <Loader size="md" /> + <p className="mt-2 text-sm text-gray-600">모임을 생성하는 중입니다...</p> + </div> </div> );src/components/common/gathering-card/scheduled-gathering-card/container.tsx (3)
Line range hint
29-67: 더미 데이터의 이메일 주소 처리가 필요합니다.더미 데이터에 실제와 유사한 이메일 주소가 포함되어 있습니다. 보안상의 이유로 다음과 같이 수정하는 것이 좋습니다:
- email: '[email protected]', + email: '[email protected]',
Line range hint
69-73: TODO 항목들의 이슈화가 필요합니다.현재 코드에 있는 TODO 코멘트들을 GitHub 이슈로 생성하여 추적하는 것이 좋습니다. 특히
useQuery관련 부분은 데이터 페칭의 중요한 부분이므로 우선적으로 처리가 필요합니다.이러한 TODO 항목들을 이슈로 생성하는 것을 도와드릴까요?
Line range hint
74-76: 좋아요 토글 기능의 개선이 필요합니다.현재
alert를 사용하여 임시로 구현된 좋아요 토글 기능을 실제 API 호출로 대체해야 합니다. 또한 사용자 피드백을 위해 토스트 메시지나 시각적 피드백을 추가하는 것이 좋습니다.const handleLikeToggle = useCallback(() => { - alert('좋아요 토글됨'); // TODO: 좋아요 토글 로직 구현 + try { + // API 호출 로직 + showToast('좋아요가 반영되었습니다'); + } catch (error) { + showToast('오류가 발생했습니다. 다시 시도해주세요.'); + } }, []);src/components/common/gathering-card/container.tsx (1)
Line range hint
52-71: 에러 처리 로직 개선을 제안드립니다.현재 에러 처리 로직이 컴포넌트 내부에 직접 구현되어 있습니다. 재사용성과 유지보수성을 높이기 위해 다음과 같은 개선을 고려해보세요:
- 에러 처리 로직을 별도의 커스텀 훅으로 분리
- 에러 메시지를 상수로 관리
다음과 같은 리팩토링을 제안드립니다:
// hooks/useApiErrorHandler.ts export const useApiErrorHandler = () => { const handleError = (error: unknown) => { if (error instanceof ApiError) { try { const errorData = JSON.parse(error.message); if (errorData.status === 'NOT_FOUND') { toast.error('모임 정보를 찾을 수 없습니다.'); return; } toast.error(`Error ${error.status}: ${error.message}`); } catch { toast.error('데이터 통신에 실패했습니다.'); } } }; return { handleError }; };컴포넌트에서는 다음과 같이 사용할 수 있습니다:
const { handleError } = useApiErrorHandler(); useEffect(() => { if (error) { handleError(error); } }, [error]);src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-form/index.tsx (2)
165-167: 날짜 데이터 처리 개선이 확인되었습니다.날짜 객체를 직접 전달하도록 변경한 것은 좋은 개선입니다. 데이터 변환 단계를 줄여 코드가 더 명확해졌습니다.
다만, 타입 안전성을 위해 date 매개변수의 타입을 명시적으로 지정하는 것을 고려해보세요:
- onChange={(date) => { + onChange={(date: Date) => {
Line range hint
84-94: 상수 값 분리 제안파일 업로드 관련 제한사항을 상수로 분리하면 유지보수성이 향상될 것 같습니다:
const FILE_SIZE_LIMIT = 5 * 1024 * 1024; // 5MB const ACCEPTED_FILE_TYPES = ['image/jpeg', 'image/jpg', 'image/png'] as const;이렇게 분리된 상수는 다음과 같이 사용할 수 있습니다:
validate: { fileSize: (file) => file && file instanceof File - ? file.size <= 5242880 || '파일 크기는 5MB 이하여야 합니다.' + ? file.size <= FILE_SIZE_LIMIT || '파일 크기는 5MB 이하여야 합니다.' : true, fileType: (file) => file && file instanceof File - ? ['image/jpeg', 'image/jpg', 'image/png'].includes(file.type) || + ? ACCEPTED_FILE_TYPES.includes(file.type as typeof ACCEPTED_FILE_TYPES[number]) || 'JPG, PNG 파일만 업로드 가능합니다.' : true,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (13)
src/_apis/gathering/gathering-detail-apis.ts(2 hunks)src/_queries/gathering/gathering-detail-queries.ts(1 hunks)src/app/(crew)/crew/_components/create-gathering-modal/container.tsx(0 hunks)src/app/(crew)/crew/create/page.tsx(1 hunks)src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-form/index.tsx(1 hunks)src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-modal/container.tsx(1 hunks)src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-modal/presenter.tsx(1 hunks)src/app/(crew)/crew/detail/[id]/_components/create-gathering/index.tsx(2 hunks)src/app/(crew)/crew/detail/[id]/edit/page.tsx(1 hunks)src/app/(crew)/crew/detail/[id]/page.tsx(1 hunks)src/components/common/gathering-card/container.tsx(1 hunks)src/components/common/gathering-card/scheduled-gathering-card/container.tsx(1 hunks)src/components/common/input/date-time-picker/index.tsx(0 hunks)
💤 Files with no reviewable changes (2)
- src/app/(crew)/crew/_components/create-gathering-modal/container.tsx
- src/components/common/input/date-time-picker/index.tsx
✅ Files skipped from review due to trivial changes (1)
- src/app/(crew)/crew/create/page.tsx
🔇 Additional comments (9)
src/_queries/gathering/gathering-detail-queries.ts (1)
1-4: 임포트 구문이 적절히 구성되어 있습니다.
필요한 모든 의존성이 올바르게 임포트되어 있으며, 각 임포트의 용도가 명확합니다.
src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-modal/presenter.tsx (1)
2-3: 임포트 경로가 적절히 구성되었습니다!
절대 경로를 사용하여 컴포넌트와 타입을 임포트하는 방식이 일관성 있게 적용되었습니다.
src/app/(crew)/crew/detail/[id]/_components/create-gathering/index.tsx (2)
6-6: 임포트 경로가 적절히 업데이트되었습니다.
컴포넌트의 새로운 위치를 반영하여 임포트 경로가 정확하게 수정되었습니다.
10-10: 타입 정의가 명확하게 되어있습니다.
crewId prop이 number 타입으로 명시적으로 정의되어 있어 타입 안정성이 보장됩니다.
src/app/(crew)/crew/detail/[id]/edit/page.tsx (1)
6-6: 컴포넌트 경로 변경이 일관성 있게 적용되었는지 확인 필요
컴포넌트 경로가 변경되었습니다. 이 변경이 다른 파일들에도 올바르게 반영되었는지 확인이 필요합니다.
✅ Verification successful
Based on the search results, I can generate the final response as both files are using the new import path consistently (@/src/app/(crew)/crew/create/_components/create-crew-form). There's no need for additional verification.
컴포넌트 경로가 일관되게 적용되어 있습니다
CreateCrewForm 컴포넌트의 import 경로가 모든 사용처에서 새로운 경로로 올바르게 적용되어 있음을 확인했습니다.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: CreateCrewForm 컴포넌트의 import 경로 사용 현황 확인
# 이전 경로 사용 여부 확인
rg "@/src/app/\(crew\)/crew/_components/create-crew-form"
# 새로운 경로 사용 여부 확인
rg "@/src/app/\(crew\)/crew/create/_components/create-crew-form"
Length of output: 384
src/_apis/gathering/gathering-detail-apis.ts (1)
2-2: 타입 임포트가 적절히 추가되었습니다!
CreateGatheringRequestTypes 타입이 새로운 함수와 함께 잘 통합되었습니다.
src/components/common/gathering-card/scheduled-gathering-card/container.tsx (1)
2-2: 임포트 경로 변경이 적절합니다!
절대 경로를 사용하여 컴포넌트를 임포트하는 방식으로 변경한 것이 좋습니다. Next.js의 라우팅 구조와도 잘 맞습니다.
src/components/common/gathering-card/container.tsx (2)
Line range hint 1-99: 전반적으로 잘 구현된 컴포넌트입니다.
컴포넌트의 구조와 기능이 잘 구현되어 있습니다:
- 상태 관리가 깔끔하게 되어 있습니다
- 날짜 처리 로직이 명확합니다
- Presenter 패턴을 잘 활용하고 있습니다
8-8: 새로운 import 경로가 올바르게 작동하는지 확인이 필요합니다.
새로운 라우팅 구조로 변경된 import 경로가 모든 상황에서 올바르게 동작하는지 확인해주세요.
✅ Verification successful
새로운 import 경로가 정상적으로 작동하는 것을 확인했습니다.
- 새 경로
@/src/app/(crew)/crew/detail/[id]/_components/gathering-detail-modal/container.tsx에 컴포넌트가 올바르게 구현되어 있습니다. - 모든 import 구문이 새로운 경로로 일관되게 업데이트되었습니다.
- 순환 참조 문제도 발견되지 않았습니다.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the new import path exists and is used consistently
# Check if the new path exists
fd "container.tsx" "src/app/(crew)/crew/detail/[id]/_components/gathering-detail-modal"
# Check for any remaining old import paths
rg "import.*GatheringDetailModalContainer.*crew/_components/gathering-detail-modal" --type ts
Length of output: 264
Script:
#!/bin/bash
# Check all imports of GatheringDetailModalContainer to ensure consistency
rg "import.*GatheringDetailModalContainer" --type ts -A 1
# Check the actual implementation exists in the new location
cat "src/app/(crew)/crew/detail/[id]/_components/gathering-detail-modal/container.tsx"
# Check for any potential circular dependencies
rg "gathering-card" "src/app/(crew)/crew/detail/[id]/_components/gathering-detail-modal/container.tsx"
Length of output: 2981
| queryKey: ['gatheringList', crewId], | ||
| refetchType: 'all', | ||
| }); | ||
| toast.success('크루가 생성되었습니다.'); |
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.
성공 메시지의 용어를 수정해야 합니다.
성공 메시지에서 "크루가 생성되었습니다"라고 되어 있는데, PR의 목적에 따르면 이는 "모임"을 생성하는 기능입니다.
다음과 같이 수정하는 것을 제안드립니다:
- toast.success('크루가 생성되었습니다.');
+ toast.success('모임이 생성되었습니다.');📝 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.
| toast.success('크루가 생성되었습니다.'); | |
| toast.success('모임이 생성되었습니다.'); |
| export async function createGathering(id: number, data: CreateGatheringRequestTypes) { | ||
| try { | ||
| await fetchApi(`/api/crews/${id}/gatherings`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| credentials: 'include', // Include authentication credentials | ||
| body: JSON.stringify(data), | ||
| }); | ||
| } catch (error) { | ||
| throw 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
API 구현에 대한 몇 가지 개선사항이 있습니다.
다음 사항들을 개선하면 좋을 것 같습니다:
- 에러 처리가 너무 일반적입니다. 구체적인 에러 케이스를 처리해주세요.
- 함수의 반환 타입이 명시되어 있지 않습니다.
id매개변수의 이름이 모호합니다.crewId로 변경하면 좋겠습니다.- 입력값 유효성 검사가 없습니다.
다음과 같이 수정하는 것을 제안드립니다:
-export async function createGathering(id: number, data: CreateGatheringRequestTypes) {
+export async function createGathering(
+ crewId: number,
+ data: CreateGatheringRequestTypes
+): Promise<void> {
+ if (!crewId || crewId <= 0) {
+ throw new Error('유효하지 않은 크루 ID입니다.');
+ }
+
try {
- await fetchApi(`/api/crews/${id}/gatherings`, {
+ await fetchApi(`/api/crews/${crewId}/gatherings`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify(data),
});
} catch (error) {
- throw error;
+ if (error instanceof Error) {
+ throw new Error(`모임 생성 중 오류 발생: ${error.message}`);
+ }
+ throw new Error('알 수 없는 오류가 발생했습니다.');
}
}📝 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.
| export async function createGathering(id: number, data: CreateGatheringRequestTypes) { | |
| try { | |
| await fetchApi(`/api/crews/${id}/gatherings`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| credentials: 'include', // Include authentication credentials | |
| body: JSON.stringify(data), | |
| }); | |
| } catch (error) { | |
| throw error; | |
| } | |
| } | |
| export async function createGathering( | |
| crewId: number, | |
| data: CreateGatheringRequestTypes | |
| ): Promise<void> { | |
| if (!crewId || crewId <= 0) { | |
| throw new Error('유효하지 않은 크루 ID입니다.'); | |
| } | |
| try { | |
| await fetchApi(`/api/crews/${crewId}/gatherings`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| credentials: 'include', | |
| body: JSON.stringify(data), | |
| }); | |
| } catch (error) { | |
| if (error instanceof Error) { | |
| throw new Error(`모임 생성 중 오류 발생: ${error.message}`); | |
| } | |
| throw new Error('알 수 없는 오류가 발생했습니다.'); | |
| } | |
| } |
| export default function CreateGatheringModalContainer({ | ||
| crewId, | ||
| opened, | ||
| close, | ||
| data, | ||
| }: CreateGatheringModalContainerProps) { | ||
| const { isPending, mutate } = useCreateGatheringQuery(crewId); |
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
에러 처리 추가 필요
useCreateGatheringQuery의 에러 상태를 처리하는 로직이 없습니다. 사용자에게 적절한 에러 메시지를 표시하는 것이 좋을 것 같습니다.
- const { isPending, mutate } = useCreateGatheringQuery(crewId);
+ const { isPending, error, mutate } = useCreateGatheringQuery(crewId);
+
+ useEffect(() => {
+ if (error) {
+ notifications.show({
+ title: '오류 발생',
+ message: '모임을 생성하는 중 문제가 발생했습니다.',
+ color: 'red',
+ });
+ }
+ }, [error]);Committable suggestion skipped: line range outside the PR's diff.
| const handleSubmit = async (createdData: CreateGatheringFormTypes) => { | ||
| let newImageUrl = createdData.imageUrl as string; | ||
| if (createdData.imageUrl instanceof File) { | ||
| const imgResponse = await getImageUrl(createdData.imageUrl, 'CREW'); | ||
| newImageUrl = imgResponse?.imageUrl as string; | ||
| } | ||
| const newData: CreateGatheringRequestTypes = { | ||
| title: createdData.title, | ||
| imageUrl: newImageUrl ?? '', | ||
| dateTime: createdData.dateTime, | ||
| location: createdData.location, | ||
| totalCount: createdData.totalCount, | ||
| introduce: createdData.introduce, | ||
| }; | ||
|
|
||
| mutate(newData); | ||
| close(); | ||
| }; |
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.
비동기 처리 및 에러 핸들링 개선 필요
mutate호출 후 즉시 모달을 닫는 것은 사용자 경험상 좋지 않을 수 있습니다.- 이미지 업로드 실패 시 에러 처리가 없습니다.
const handleSubmit = async (createdData: CreateGatheringFormTypes) => {
try {
let newImageUrl = createdData.imageUrl as string;
if (createdData.imageUrl instanceof File) {
const imgResponse = await getImageUrl(createdData.imageUrl, 'CREW');
+ if (!imgResponse?.imageUrl) {
+ throw new Error('이미지 업로드 실패');
+ }
newImageUrl = imgResponse?.imageUrl as string;
}
const newData: CreateGatheringRequestTypes = {
title: createdData.title,
imageUrl: newImageUrl ?? '',
dateTime: createdData.dateTime,
location: createdData.location,
totalCount: createdData.totalCount,
introduce: createdData.introduce,
};
- mutate(newData);
- close();
+ await mutate(newData);
+ close();
+ } catch (error) {
+ notifications.show({
+ title: '오류 발생',
+ message: '모임 생성에 실패했습니다. 다시 시도해주세요.',
+ color: 'red',
+ });
+ }
};📝 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 handleSubmit = async (createdData: CreateGatheringFormTypes) => { | |
| let newImageUrl = createdData.imageUrl as string; | |
| if (createdData.imageUrl instanceof File) { | |
| const imgResponse = await getImageUrl(createdData.imageUrl, 'CREW'); | |
| newImageUrl = imgResponse?.imageUrl as string; | |
| } | |
| const newData: CreateGatheringRequestTypes = { | |
| title: createdData.title, | |
| imageUrl: newImageUrl ?? '', | |
| dateTime: createdData.dateTime, | |
| location: createdData.location, | |
| totalCount: createdData.totalCount, | |
| introduce: createdData.introduce, | |
| }; | |
| mutate(newData); | |
| close(); | |
| }; | |
| const handleSubmit = async (createdData: CreateGatheringFormTypes) => { | |
| try { | |
| let newImageUrl = createdData.imageUrl as string; | |
| if (createdData.imageUrl instanceof File) { | |
| const imgResponse = await getImageUrl(createdData.imageUrl, 'CREW'); | |
| if (!imgResponse?.imageUrl) { | |
| throw new Error('이미지 업로드 실패'); | |
| } | |
| newImageUrl = imgResponse?.imageUrl as string; | |
| } | |
| const newData: CreateGatheringRequestTypes = { | |
| title: createdData.title, | |
| imageUrl: newImageUrl ?? '', | |
| dateTime: createdData.dateTime, | |
| location: createdData.location, | |
| totalCount: createdData.totalCount, | |
| introduce: createdData.introduce, | |
| }; | |
| await mutate(newData); | |
| close(); | |
| } catch (error) { | |
| notifications.show({ | |
| title: '오류 발생', | |
| message: '모임 생성에 실패했습니다. 다시 시도해주세요.', | |
| color: 'red', | |
| }); | |
| } | |
| }; |
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
🧹 Outside diff range and nitpick comments (1)
src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-form/index.tsx (1)
177-179: 날짜 유효성 검사를 추가하면 좋을 것 같습니다.현재는 기본적인 필수값 검사만 되어있습니다. 과거 날짜 선택 방지나 유효한 날짜 범위 설정 등의 추가적인 유효성 검사를 고려해보시면 좋을 것 같습니다.
예시 코드입니다:
rules={{ required: '날짜와 시간을 선택해주세요.', validate: { isFuture: (date) => date > new Date() || '과거 날짜는 선택할 수 없습니다.', isWithinRange: (date) => date < new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) || '최대 30일 이내의 날짜만 선택 가능합니다.', }, }}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-form/index.tsx(4 hunks)src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-modal/container.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-modal/container.tsx
🔇 Additional comments (1)
src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-form/index.tsx (1)
42-50: 이미지 업로드 처리가 잘 구현되었습니다!
비동기 처리와 타입 체크가 적절하게 구현되어 있습니다. 파일 업로드 실패 시 빈 문자열을 반환하는 처리도 안전합니다.
| handleFileChange(newValue, field.onChange); | ||
| field.onChange(newValue); | ||
| trigger('imageUrl'); |
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.
onChange 핸들러의 중복 호출 문제를 수정해주세요.
현재 코드에서는 handleFileChange와 field.onChange가 연속적으로 호출되어 잠재적인 상태 불일치 문제가 발생할 수 있습니다.
다음과 같이 수정하는 것을 추천드립니다:
-handleFileChange(newValue, field.onChange);
-field.onChange(newValue);
-trigger('imageUrl');
+handleFileChange(newValue, (value) => {
+ field.onChange(value);
+ trigger('imageUrl');
+});📝 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.
| handleFileChange(newValue, field.onChange); | |
| field.onChange(newValue); | |
| trigger('imageUrl'); | |
| handleFileChange(newValue, (value) => { | |
| field.onChange(value); | |
| trigger('imageUrl'); | |
| }); |
minkyung5x5
left a comment
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.
수고많으셨습니다!!!!
🔖 Issue Ticket
#132
✍️ Description
약속 생성하기 기능 API 연결했습니다~!
✅ Checklist
PR
Test
Summary by CodeRabbit
New Features
Bug Fixes
Chores