-
Notifications
You must be signed in to change notification settings - Fork 1
[Feature/#199] 나의 푸드트럭 등록 / 수정 api / 리뷰 XXXX #214
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,10 +1,15 @@ | ||||||
| import { zodResolver } from '@hookform/resolvers/zod'; | ||||||
| import { useEffect, useState } from 'react'; | ||||||
| import { useForm } from 'react-hook-form'; | ||||||
| import { zodResolver } from '@hookform/resolvers/zod'; | ||||||
|
|
||||||
| import { formatStringDatesToAvailableDates } from '@utils/date'; | ||||||
| import { FOOD_TRUCK_ERROR_MESSAGE } from '@pages/@owner/food-truck-form/constants/food-truck'; | ||||||
| import { | ||||||
| foodTruckSchema, | ||||||
| type FoodTruckFormData, | ||||||
| } from '@pages/@owner/food-truck-form/schemas/food-truck-form.schema'; | ||||||
| import useFoodTruckDetail from '@pages/food-truck-detail/hooks/use-food-truck-detail'; | ||||||
| import { useMenusQuery } from '@pages/@owner/menu/hooks/use-menus-query'; | ||||||
|
|
||||||
| const initialData = { | ||||||
| name: '', | ||||||
|
|
@@ -25,10 +30,10 @@ const initialData = { | |||||
| menus: false, | ||||||
| }; | ||||||
|
|
||||||
| export const useFoodTruckForm = (prevData?: FoodTruckFormData) => { | ||||||
| export const useFoodTruckForm = (foodTruckIdNumber: number) => { | ||||||
| const methods = useForm<FoodTruckFormData>({ | ||||||
| resolver: zodResolver(foodTruckSchema), | ||||||
| defaultValues: prevData ?? initialData, | ||||||
| defaultValues: initialData, | ||||||
| mode: 'onChange', | ||||||
| }); | ||||||
|
|
||||||
|
|
@@ -39,6 +44,41 @@ export const useFoodTruckForm = (prevData?: FoodTruckFormData) => { | |||||
| setError, | ||||||
| } = methods; | ||||||
|
|
||||||
| // 기존 데이터가 있다면 수정 mode | ||||||
| const [isEdit, setIsEdit] = useState(false); | ||||||
|
|
||||||
| // 기존 등록 푸드트럭 데이터 조회 | ||||||
| const { foodTruckDetailData } = useFoodTruckDetail(foodTruckIdNumber); | ||||||
|
|
||||||
| // 메뉴 등록 여부를 위한 조회 | ||||||
| const { data: menuData } = useMenusQuery(foodTruckIdNumber, '최신순'); | ||||||
|
|
||||||
| useEffect(() => { | ||||||
| if (foodTruckDetailData) { | ||||||
| setIsEdit(true); | ||||||
| reset({ | ||||||
| name: foodTruckDetailData.name, | ||||||
| nameDuplicate: true, | ||||||
| description: foodTruckDetailData.description, | ||||||
| phoneNumber: foodTruckDetailData.phoneNumber, | ||||||
| regionCodes: foodTruckDetailData.regionCodes, | ||||||
| availableQuantity: foodTruckDetailData.availableQuantity, | ||||||
| needElectricity: foodTruckDetailData.needElectricity, | ||||||
| paymentMethod: foodTruckDetailData.paymentMethod, | ||||||
| menuCategories: foodTruckDetailData.menuCategories, | ||||||
| photoUrls: foodTruckDetailData.photoUrl, | ||||||
| operatingInfo: foodTruckDetailData.operatingInfo, | ||||||
| option: foodTruckDetailData.option, | ||||||
| availableDates: formatStringDatesToAvailableDates( | ||||||
| foodTruckDetailData.availableDates ?? [] | ||||||
| ), | ||||||
| activeTime: foodTruckDetailData.activeTime, | ||||||
| timeDiscussRequired: foodTruckDetailData.timeDiscussRequired, | ||||||
| menus: menuData !== undefined, | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 메뉴 존재 여부 체크 로직을 개선해주세요.
🔎 메뉴 존재 여부를 정확히 체크하는 수정 제안- menus: menuData !== undefined,
+ menus: menuData?.pages?.[0]?.content?.length > 0,또는 더 안전하게: - menus: menuData?.pages.some(page => page.content?.length > 0) ?? false,📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| }); | ||||||
| } | ||||||
| }, [foodTruckDetailData, menuData, reset]); | ||||||
|
|
||||||
| const onSubmit = async (formData: FoodTruckFormData) => { | ||||||
| if (!formData.nameDuplicate) { | ||||||
| setError('name', { | ||||||
|
|
@@ -47,13 +87,14 @@ export const useFoodTruckForm = (prevData?: FoodTruckFormData) => { | |||||
| return; | ||||||
| } | ||||||
| if (isValid && formData) { | ||||||
| //TODO: 계좌 등록 제출 | ||||||
| // TODO: 푸드트럭 등록 api 호출 | ||||||
| alert('푸드트럭 등록 제출'); | ||||||
| } | ||||||
| }; | ||||||
|
|
||||||
| return { | ||||||
| // Form methods | ||||||
| isEdit, | ||||||
| methods, | ||||||
| handleSubmit: handleSubmit(onSubmit), | ||||||
| reset, | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,10 +5,7 @@ import { | |
| FOOD_TRUCK_ERROR_MESSAGE, | ||
| FOOD_TRUCK_MAX_LENGTH, | ||
| } from '@pages/@owner/food-truck-form/constants/food-truck'; | ||
| import { AVAILABLE_QUANTITY } from '@constant/available-quantity'; | ||
| import { NEED_ELECTRICITY } from '@constant/need-electricity'; | ||
| import { PAYMENT_METHOD } from '@constant/payment-method'; | ||
| import { FOOD_CATEGORIES } from '@constant/food-categories'; | ||
|
|
||
| import type { AvailableDate } from '@type/available-date'; | ||
| import { validateFoodTruckFormTime } from '@pages/@owner/food-truck-form/utils/validate-food-truck-form-time'; | ||
|
|
||
|
|
@@ -34,10 +31,10 @@ export const foodTruckSchema = z.object({ | |
| FOOD_TRUCK_ERROR_MESSAGE.phoneNumber.required | ||
| ), | ||
| regionCodes: z.array(z.custom<RegionResponse>()), | ||
| availableQuantity: z.nativeEnum(AVAILABLE_QUANTITY), | ||
| needElectricity: z.nativeEnum(NEED_ELECTRICITY), | ||
| paymentMethod: z.nativeEnum(PAYMENT_METHOD), | ||
| menuCategories: z.array(z.nativeEnum(FOOD_CATEGORIES)), | ||
| availableQuantity: z.string(), | ||
| needElectricity: z.string(), | ||
| paymentMethod: z.string(), | ||
| menuCategories: z.array(z.string()), | ||
|
Comment on lines
+34
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 타입 안정성 손실을 해결해주세요.
의도적인 변경이라면 다음 중 하나를 권장합니다:
🔎 제안하는 개선안 (예시: needElectricity)+import { NEED_ELECTRICITY } from '@shared/constant/need-electricity';
+
+const needElectricityValues = Object.values(NEED_ELECTRICITY) as [string, ...string[]];
+
export const foodTruckSchema = z.object({
// ... other fields
- needElectricity: z.string(),
+ needElectricity: z.enum(needElectricityValues, {
+ error: () => ({ code: 'custom', message: '올바른 전기 사용 옵션을 선택해주세요.' }),
+ }),다른 필드들도 유사하게 처리할 수 있습니다.
|
||
| photoUrls: z.array(z.string()).min(1, { | ||
| message: FOOD_TRUCK_ERROR_MESSAGE.photoUrls.required, | ||
| }), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| export const NEED_ELECTRICITY = { | ||
| REQUIRED: '필요', | ||
| NOT_REQUIRED: '필요 없음', | ||
| NOT_REQUIRED: '불필요', | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Search for needElectricity usage across the codebase
rg -nP --type=ts --type=tsx -C3 'needElectricity|NEED_ELECTRICITY|need-electricity' -g '!node_modules'Repository: CHA-CALL/ChaCall-FE Length of output: 90 🏁 Script executed: #!/bin/bash
# Search for needElectricity usage across the codebase
# Using glob pattern to include .ts and .tsx files
rg 'needElectricity|NEED_ELECTRICITY|need-electricity' -g '*.ts' -g '*.tsx' -C3Repository: CHA-CALL/ChaCall-FE Length of output: 23723 Zod 스키마에서 needElectricity 유효성 검증 누락
needElectricity: z.enum(['필요', '불필요', '논의 필요'])또한 예상과 다르게 🤖 Prompt for AI Agents |
||
| NEED_DISCUSSION: '논의 필요', | ||
| } as const; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| export const PAYMENT_METHOD = { | ||
| CARD: '카드', | ||
| BANK_TRANSFER: '계좌 이체', | ||
| ANY: '아무거나', | ||
| BANK_TRANSFER: '계좌이체', | ||
| ANY: '무관', | ||
|
Comment on lines
+3
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find usages of paymentMethod and PAYMENT_METHOD across the codebase
rg -nP --type=ts --type=tsx -C3 'paymentMethod|PAYMENT_METHOD' -g '!node_modules'Repository: CHA-CALL/ChaCall-FE Length of output: 90 🏁 Script executed: #!/bin/bash
# Search for paymentMethod and PAYMENT_METHOD usage without tsx type
rg -nP 'paymentMethod|PAYMENT_METHOD' -C3 -g '!node_modules'Repository: CHA-CALL/ChaCall-FE Length of output: 16300 🏁 Script executed: #!/bin/bash
# Search for old payment method values that may have changed
rg '계좌 이체|아무거나' -g '!node_modules'Repository: CHA-CALL/ChaCall-FE Length of output: 45 🏁 Script executed: #!/bin/bash
# Read the food-truck-form.schema.ts file
head -100 src/pages/@owner/food-truck-form/schemas/food-truck-form.schema.tsRepository: CHA-CALL/ChaCall-FE Length of output: 2146 🏁 Script executed: #!/bin/bash
# Search for any hardcoded payment method comparisons or mappings
rg 'BANK_TRANSFER|CARD|ANY' -C2 src/shared/constant/payment-method.tsRepository: CHA-CALL/ChaCall-FE Length of output: 159 기존 데이터와의 호환성을 확인해주세요.
백엔드 API 계약서에는 이미 새로운 값('무관', '계좌이체', '카드')이 정의되어 있어 새 값을 수신하는 데는 문제가 없습니다. 그러나 데이터베이스에 존재하는 기존 푸드트럭 데이터가 여전히 이전 문자열 값을 가지고 있을 수 있으므로 다음을 확인해주세요:
|
||
| } as const; | ||
|
|
||
| export type PaymentMethodKey = keyof typeof PAYMENT_METHOD; | ||
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.
Hooks 규칙 위반 및 로직 흐름 문제를 수정해주세요.
현재 구조는 다음 문제들이 있습니다:
Hooks 규칙 위반: 유효성 검증(line 69-73)이 hooks 호출(line 43-61) 이후에 위치하여, 조건부로 hooks를 건너뛰게 됩니다. Hooks는 항상 동일한 순서로 호출되어야 합니다.
로직 흐름 문제:
navigate()는 비동기적으로 작동하므로return null전에 컴포넌트가 잠깐 렌더링될 수 있으며, 토스트 메시지가 표시되지 않을 수 있습니다.사용자 경험: 잘못된 접근 시 빈 화면이 깜빡일 수 있습니다.
🔎 제안하는 수정
방법 1: Early return을 hooks 전에 배치
export default function FoodTruckForm() { const { foodTruckId } = useParams(); - const foodTruckIdNumber = Number(foodTruckId); - const navigate = useNavigate(); const location = useLocation(); const toast = useToast(); + // Hooks 호출 전에 early return + if (!foodTruckId || isNaN(Number(foodTruckId))) { + useEffect(() => { + toast.error('잘못된 접근입니다.'); + navigate(ROUTES.FOOD_TRUCK_MANAGEMENT); + }, [navigate, toast]); + return null; + } + + const foodTruckIdNumber = Number(foodTruckId); + const { isEdit, methods, reset, isFormValid, handleSubmit } = useFoodTruckForm(foodTruckIdNumber); // ... rest of hooks - if (!foodTruckId || isNaN(foodTruckIdNumber)) { - toast.error('잘못된 접근입니다.'); - navigate(ROUTES.FOOD_TRUCK_MANAGEMENT); - return null; - }방법 2: useEffect로 검증 처리 (더 권장)
export default function FoodTruckForm() { const { foodTruckId } = useParams(); const foodTruckIdNumber = Number(foodTruckId); const navigate = useNavigate(); const location = useLocation(); const toast = useToast(); + // 유효성 검증을 useEffect로 처리 + useEffect(() => { + if (!foodTruckId || isNaN(foodTruckIdNumber)) { + toast.error('잘못된 접근입니다.'); + navigate(ROUTES.FOOD_TRUCK_MANAGEMENT); + } + }, [foodTruckId, foodTruckIdNumber, navigate, toast]); + + // 잘못된 접근인 경우 로딩 상태 표시 + if (!foodTruckId || isNaN(foodTruckIdNumber)) { + return null; // 또는 <LoadingSpinner /> 등 + } const { isEdit, methods, reset, isFormValid, handleSubmit } = useFoodTruckForm(foodTruckIdNumber); // ... rest of the component - if (!foodTruckId || isNaN(foodTruckIdNumber)) { - toast.error('잘못된 접근입니다.'); - navigate(ROUTES.FOOD_TRUCK_MANAGEMENT); - return null; - }