diff --git a/src/pages/dashboard/ui/EventInfoPage.tsx b/src/pages/dashboard/ui/EventInfoPage.tsx index ffaf4686..281503c3 100644 --- a/src/pages/dashboard/ui/EventInfoPage.tsx +++ b/src/pages/dashboard/ui/EventInfoPage.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from 'react'; +import { useQueryClient } from '@tanstack/react-query'; import ChoiceChip from '../../../../design-system/ui/ChoiceChip'; import DefaultTextField from '../../../../design-system/ui/textFields/DefaultTextField'; import EventDatePicker from '../../../features/event/ui/DatePicker'; @@ -10,7 +11,7 @@ import { AddressSearch } from '../../../shared/ui/AddressSearch'; import KakaoMap from '../../../shared/ui/KakaoMap'; import { UpdateEventRequest } from '../../../features/dashboard/model/event'; import { useEventDetail } from '../../../entities/event/hook/useEventHook'; -import { useQueryClient } from '@tanstack/react-query'; +import { formatPhoneNumber } from '../../../shared/utils/phoneFormatter'; const EventInfoPage = () => { const queryClient = useQueryClient(); @@ -54,7 +55,7 @@ const EventInfoPage = () => { category: data.result.category || 'DEVELOPMENT_STUDY', hashtags: data.result.hashtags || [], organizerEmail: email || data.result.organizerEmail || '', - organizerPhoneNumber: phone || data.result.organizerPhoneNumber || '', + organizerPhoneNumber: phone.replace(/-/g, '') || data.result.organizerPhoneNumber || '', }; mutate(requestData, { @@ -113,10 +114,10 @@ const EventInfoPage = () => { /> setPhone(e.target.value)} + onChange={e => setPhone(formatPhoneNumber(e.target.value))} /> void; @@ -13,6 +14,7 @@ const EventOrganizerInfoPage = ({ onValidationChange }: EventOrganizerInfoPagePr const { register, + setValue, watch, formState: { errors, isValid }, } = useForm({ @@ -27,6 +29,11 @@ const EventOrganizerInfoPage = ({ onValidationChange }: EventOrganizerInfoPagePr const phoneValue = watch('phone'); const emailValue = watch('email'); + const handlePhoneChange = (e: React.ChangeEvent) => { + const formatted = formatPhoneNumber(e.target.value); + setValue('phone', formatted, { shouldValidate: true }); + }; + useEffect(() => { onValidationChange?.(isValid); }, [isValid, onValidationChange]); @@ -50,12 +57,13 @@ const EventOrganizerInfoPage = ({ onValidationChange }: EventOrganizerInfoPagePr {...register('email')} /> ); diff --git a/src/pages/join/InfoInputPage.tsx b/src/pages/join/InfoInputPage.tsx index 1418fa0f..cc8f574c 100644 --- a/src/pages/join/InfoInputPage.tsx +++ b/src/pages/join/InfoInputPage.tsx @@ -1,12 +1,13 @@ +import { useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; import { useForm, SubmitHandler } from 'react-hook-form'; import Header from '../../../design-system/ui/Header'; import Button from '../../../design-system/ui/Button'; import UnderlineTextField from '../../../design-system/ui/textFields/UnderlineTextField'; -import { useNavigate } from 'react-router-dom'; import { FormData, zodValidation } from '../../shared/lib/formValidation'; import { useUserInfo, useUserUpdate } from '../../features/join/hooks/useUserHook'; import useAuthStore from '../../app/provider/authStore'; -import { useEffect } from 'react'; +import { formatPhoneNumber } from '../../shared/utils/phoneFormatter'; const InfoInputPage = () => { const { data, isLoading } = useUserInfo(); @@ -17,6 +18,8 @@ const InfoInputPage = () => { handleSubmit, formState: { errors, isValid }, reset, + setValue, + watch, } = useForm({ mode: 'onChange', defaultValues: { @@ -28,20 +31,27 @@ const InfoInputPage = () => { }); const navigate = useNavigate(); + const phoneValue = watch('phone'); + + const handlePhoneChange = (e: React.ChangeEvent) => { + const formatted = formatPhoneNumber(e.target.value); + setValue('phone', formatted, { shouldValidate: true }); + }; + const onSubmit: SubmitHandler = formData => { const updatedData = { - name: data?.name || "", - email: data?.email || "", + name: data?.name || '', + email: data?.email || '', phoneNumber: formData.phone, }; updateUser(updatedData, { onSuccess: () => { login(); - setName(data?.name || "사용자"); + setName(data?.name || '사용자'); alert('정보가 성공적으로 업데이트되었습니다.'); navigate('/'); }, - onError: (err) => { + onError: err => { alert('정보 업데이트에 실패했습니다. 다시 시도해주세요.'); console.error(err); }, @@ -81,11 +91,12 @@ const InfoInputPage = () => { {/* 연락처 필드 */} {/* 이메일 필드 */} diff --git a/src/shared/lib/formValidation.ts b/src/shared/lib/formValidation.ts index b356962d..100b88f5 100644 --- a/src/shared/lib/formValidation.ts +++ b/src/shared/lib/formValidation.ts @@ -9,10 +9,7 @@ export const formSchema = z.object({ .regex(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/, '올바른 이메일 형식이어야 합니다.'), phone: z .string() - .regex(/^[0-9]{3}-[0-9]{3,4}-[0-9]{4}$/, '연락처는 하이픈(-)을 포함한 형식이어야 합니다.') - .refine(val => val.length === 13, { - message: '연락처는 하이픈(-) 포함 13자 형식(예: 010-1234-5678)이어야 합니다.', - }), + .regex(/^[0-9]{3}-[0-9]{3,4}-[0-9]{4}$/, '연락처는 휴대전화 번호 형식(예: 010-1234-5678)이어야 합니다.') }); export const organizerFormSchema = formSchema.pick({ email: true, phone: true }); export const eventTitleSchema = z.object({ diff --git a/src/shared/utils/phoneFormatter.ts b/src/shared/utils/phoneFormatter.ts new file mode 100644 index 00000000..091de0d5 --- /dev/null +++ b/src/shared/utils/phoneFormatter.ts @@ -0,0 +1,6 @@ +export const formatPhoneNumber = (value: string) => { + const numbers = value.replace(/[^\d]/g, '').slice(0, 11); // 11자리까지만 허용 + if (numbers.length <= 3) return numbers; + if (numbers.length <= 7) return `${numbers.slice(0, 3)}-${numbers.slice(3)}`; + return `${numbers.slice(0, 3)}-${numbers.slice(3, 7)}-${numbers.slice(7, 11)}`; +}; \ No newline at end of file