diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index f2f4dd7..3f5a91d 100644 Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ diff --git a/package.json b/package.json index 68b5159..a4317bd 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "test:watch": "jest --watchAll" }, "dependencies": { + "@hookform/resolvers": "^3.9.0", "@next/bundle-analyzer": "^13.4.19", "@nextui-org/skeleton": "^2.0.32", "axios": "^1.3.5", @@ -26,7 +27,8 @@ "recoil": "^0.7.7", "recoil-persist": "^4.2.0", "tailwind-styled-components": "^2.2.0", - "ts-node": "^10.9.2" + "ts-node": "^10.9.2", + "yup": "^1.4.0" }, "devDependencies": { "@testing-library/dom": "^10.4.0", diff --git a/src/api/common/axios.ts b/src/api/common/axios.ts index c6613e0..96bd69e 100644 --- a/src/api/common/axios.ts +++ b/src/api/common/axios.ts @@ -34,6 +34,11 @@ client.interceptors.response.use( }, async function (error: any) { const originalRequest = error.config; + let retryFlg = false; + + if (retryFlg) { + return Promise.reject(error); // 이미 재시도된 요청은 다시 재시도하지 않음 + } // 토큰 만료 시 처리 if (error.response && error.response.data.message === '유효하지 않은 토큰입니다.') { @@ -51,17 +56,24 @@ client.interceptors.response.use( refreshToken: refreshToken, }; - await fetch('http://localhost:8080/api/set-cookies', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', + console.log(accessToken); + console.log(loginUserCookiesData); + + await axios.post( + 'http://localhost:8080/api/set-cookies', + JSON.stringify(newCookiesData), + { + headers: { + 'Content-Type': 'application/json', + }, }, - body: JSON.stringify(newCookiesData), - }); + ); // 3. 갱신된 토큰을 헤더에 추가 originalRequest.headers['Authorization'] = `Bearer ${accessToken}`; + retryFlg = true; + // 4. 원래 요청 재시도 return client(originalRequest); } else { diff --git a/src/app/wishes/create/page.tsx b/src/app/wishes/create/page.tsx index 767ed1c..5d1b26f 100644 --- a/src/app/wishes/create/page.tsx +++ b/src/app/wishes/create/page.tsx @@ -14,6 +14,7 @@ export default function WishesCreatePage({ searchParams: { step: WishesCreateStepType; wishTitle: string }; }) { const { step, wishTitle } = searchParams; + if ((step !== 'link' && step !== 'account') || wishTitle === '') { return ; } diff --git a/src/components/Common/Calendar/Calendar.tsx b/src/components/Common/Calendar/Calendar.tsx index 721189e..538df11 100644 --- a/src/components/Common/Calendar/Calendar.tsx +++ b/src/components/Common/Calendar/Calendar.tsx @@ -1,40 +1,20 @@ import Image from 'next/image'; import { ko } from 'date-fns/locale'; -import { UseFormReturn, useWatch } from 'react-hook-form'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import CalendarHeader from './CalendarHeader'; import { getDate } from '@/utils/common/getDate'; import { CalendarGreyIc, CalendarIc } from '../../../../public/assets/icons'; - import Box from '../Box'; -import { WishesLinkDataType } from '@/types/input'; interface CalendarProps { date: Date; - methods: UseFormReturn; + handleChangeDate?: (selectedDate: Date) => void; readOnly?: boolean; } export default function Calendar(props: CalendarProps) { - const { date, methods, readOnly } = props; - - const control = methods.control; - - const watchStartDateData = useWatch({ - control, - name: 'startDate', - }); - - const watchEndDateData = useWatch({ - control, - name: 'endDate', - }); - - const handleChangeDate = (selectedDate: Date) => { - methods.setValue('startDate', selectedDate); - methods.setValue('endDate', getDate(selectedDate, 7)); - }; + const { date, handleChangeDate, readOnly } = props; return ( diff --git a/src/components/UI/InputTextForm.tsx b/src/components/UI/InputTextForm.tsx index 5a40d20..78263fd 100644 --- a/src/components/UI/InputTextForm.tsx +++ b/src/components/UI/InputTextForm.tsx @@ -1,42 +1,46 @@ -import { useFormContext, useWatch } from 'react-hook-form'; +import { RegisterOptions, useFormContext, UseFormRegisterReturn, useWatch } from 'react-hook-form'; import InputForm from './InputForm'; import InputText from '../Common/Input/inputText'; import InputTextarea from '../Common/Input/inputTextarea'; -import { memo, ReactNode } from 'react'; +import { ReactNode } from 'react'; export default function InputTextForm({ inputType, inputTitle, + register, registerName, + validate, placeholder, maxLength, children, }: { inputType: 'text' | 'textarea'; inputTitle: string; - registerName: keyof T; + register: UseFormRegisterReturn; + registerName?: keyof T; + validate?: RegisterOptions; placeholder?: string; maxLength?: number; children?: ReactNode; }) { - const { register, control } = useFormContext(); + // const { register, control } = useFormContext(); - const text = useWatch({ - control, - name: registerName as string, - }) as string; + // const text = useWatch({ + // control, + // name: registerName as string, + // }) as string; return ( {inputType === 'text' ? ( - + {children} - {maxLength && } + {/* {maxLength && } */} ) : ( - + {children} - {maxLength && } + {/* {maxLength && } */} )} diff --git a/src/components/UI/UploadImageBox.tsx b/src/components/UI/UploadImageBox.tsx index f83c146..bf874d9 100644 --- a/src/components/UI/UploadImageBox.tsx +++ b/src/components/UI/UploadImageBox.tsx @@ -1,36 +1,68 @@ 'use client'; +// import Image from 'next/image'; +// import { UploadImageLogoIc } from '../../../public/assets/icons'; + +// export function UploadImageBox({ +// imageUrl, +// handleUploadImageFile, +// }: { +// imageUrl: string; +// handleUploadImageFile?: (e: React.ChangeEvent) => void; +// }) { +// return ( +// +// ); +// } + +import React from 'react'; import Image from 'next/image'; import { UploadImageLogoIc } from '../../../public/assets/icons'; -import { Suspense } from 'react'; -export function UploadImageBox({ - imageURL, +export const UploadImageBox = React.memo(function UploadImageBox({ + imageUrl, handleUploadImageFile, }: { - imageURL: string; + imageUrl: string; handleUploadImageFile?: (e: React.ChangeEvent) => void; }) { return ( - - + + ); -} +}); diff --git a/src/constant/init.ts b/src/constant/init.ts new file mode 100644 index 0000000..344c835 --- /dev/null +++ b/src/constant/init.ts @@ -0,0 +1,19 @@ +import { WishesAccountDataType, WishesLinkDataType } from '@/types/input'; +import { getDate } from '@/utils/common/getDate'; + +export const wishesLinkInputInit: WishesLinkDataType = { + imageUrl: '', + title: '', + hint: '', + startDate: new Date(), + endDate: getDate(new Date(), 7), + wantsGift: false, +}; + +export const wishesAccountInputInit: WishesAccountDataType = { + name: '', + account: '', + bank: '', + phone: '', + noticeAgree: false, +}; diff --git a/src/container/login/oauth2/code/kakao/container.tsx b/src/container/login/oauth2/code/kakao/container.tsx index daf171b..24169b0 100644 --- a/src/container/login/oauth2/code/kakao/container.tsx +++ b/src/container/login/oauth2/code/kakao/container.tsx @@ -2,6 +2,7 @@ import Loading from '@/app/loading'; import { LoginUserDataType } from '@/utils/common/cookies'; +import axios from 'axios'; import { useRouter } from 'next/navigation'; import { useEffect } from 'react'; @@ -11,19 +12,18 @@ export default function LoginContainer({ loginUserData }: { loginUserData?: Logi if (!loginUserData) { alert('카카오 로그인 에러'); router.push('/'); - return ; } useEffect(() => { - fetch('http://localhost:8080/api/set-cookies', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(loginUserData), - }).then(() => { - router.push('/wishes'); - }); + axios + .post('http://localhost:8080/api/set-cookies', JSON.stringify(loginUserData), { + headers: { + 'Content-Type': 'application/json', + }, + }) + .then(() => { + router.push('/wishes'); + }); }, []); return ; diff --git a/src/container/present/server.tsx b/src/container/present/server.tsx index d24ee29..134b8a9 100644 --- a/src/container/present/server.tsx +++ b/src/container/present/server.tsx @@ -37,7 +37,7 @@ export function MessageFromWisheMaker({ wishId }: { wishId: string }) { {/* 이미지값 넣어줘야해요! */}
- +
); diff --git a/src/container/wishes/create/createInputForm.tsx b/src/container/wishes/create/WishesAccountInputForm.tsx similarity index 52% rename from src/container/wishes/create/createInputForm.tsx rename to src/container/wishes/create/WishesAccountInputForm.tsx index b3e5dd3..4e1e166 100644 --- a/src/container/wishes/create/createInputForm.tsx +++ b/src/container/wishes/create/WishesAccountInputForm.tsx @@ -2,184 +2,18 @@ import { FormProvider, useForm, UseFormReturn, useWatch } from 'react-hook-form'; import InputForm from '@/components/UI/InputForm'; -import Calendar from '@/components/Common/Calendar/Calendar'; - import { useEffect, useState } from 'react'; -import { useUploadItemInfo } from '@/hooks/wishes/useUploadItemInfo'; -import { UploadImageBox } from '@/components/UI/UploadImageBox'; -import { - wishesAccountDataValidate, - wishesLinkDataValidate, -} from '@/validation/wishes-register-options'; import Button from '@/components/Common/Button'; import { AccountInfoType } from '@/types/wishesType'; import InputText from '@/components/Common/Input/inputText'; import CheckBox from '@/components/UI/CheckBox'; import BankModal from '@/components/Common/Modal/BankModal'; -import RadioSelect from '@/components/UI/RadioSelect'; -import DropDwonBox from '@/components/UI/DropDwonBox'; import useToggle from '@/hooks/common/useToggle'; import Modal from '@/components/Common/Modal'; import { colors } from '@/styles/styles'; -import dynamic from 'next/dynamic'; import { WishesAccountDataType, WishesLinkDataType } from '@/types/input'; -import { MAX_TEXTAREA_LENGTH } from '@/constant/input'; -import InputTextForm from '@/components/UI/InputTextForm'; import { useRouter } from 'next/navigation'; import { postVerifyAccount } from '@/api/user'; -import { postWishes } from '@/api/wishes'; - -const DropDownContent = dynamic(() => import('./dropdownContent')); - -export function WishesLinkInputForm({ - methods, - progressWishesLinkData, -}: { - methods: UseFormReturn; - progressWishesLinkData?: WishesLinkDataType; -}) { - const { imageURL, setImageURL, uploadImageFile } = useUploadItemInfo(); - const { toggleState: dropDownState, handleToggle, changeOpenState } = useToggle(); - const router = useRouter(); - const control = methods.control; - const watchWantsGift = useWatch({ - control, - name: 'wantsGift', - }); - - const watchImageUrl = useWatch({ - control, - name: 'imageUrl', - }); - - useEffect(() => { - if (progressWishesLinkData) { - methods.reset({ - ...progressWishesLinkData, - }); - return; - } - }, []); - - useEffect(() => { - // if (methods.formState.isValid && preSignedImageUrl) { - // changeNextBtnDisabledState(false); - // } else { - // changeNextBtnDisabledState(true); - // } - // changeNextBtnDisabledState(false); - }, [methods.formState.isValid, imageURL]); - - useEffect(() => { - if (imageURL) { - methods.setValue('imageUrl', imageURL); - } - }, [imageURL]); - - function handleNextStep() { - const wishTitle = methods.getValues('title'); - - if (watchWantsGift) { - router.push(`/wishes/create?step=account&wishTitle=${wishTitle}`); - - // nextStep(); - } else { - createOnlyLettersWishes(); - } - } - - function createOnlyLettersWishes() { - try { - postWishes(methods).then((response) => { - if (response.data.success) { - router.push('/wishes/share'); - } - }); - } catch (error) {} - } - - console.log(imageURL); - - return ( - <> - -
{})}> - - - - - {/* - inputType="textarea" - inputTitle="친구에게 남기고 싶은 한마디" - registerName="hint" - placeholder="ex.) 생일을 축하합니다~" - maxLength={MAX_TEXTAREA_LENGTH} - /> - - -
- - -
-
- - -
    -
  • { - methods.setValue('wantsGift', true); - }} - > - - - 네! 생일 선물도 받아볼래요 - - {dropDownState && } -
  • - -
  • { - methods.setValue('wantsGift', false); - changeOpenState(false); - }} - > - - 아니요. 편지만 받을래요! -
  • -
-
- -
- {watchWantsGift && ( - - )} - - -
*/} - -
- - ); -} export default function WishesAccountInput({ accountData, @@ -198,18 +32,9 @@ export default function WishesAccountInput({ const router = useRouter(); const control = methods.control; - - const watchAccount = useWatch({ - control, - name: 'account', - }); - const watchName = useWatch({ - control, - name: 'name', - }); - const watchBank = useWatch({ + const [accountWatch, nameWatch, bankWatch] = useWatch({ control, - name: 'bank', + name: ['account', 'name', 'bank'], }); useEffect(() => { @@ -219,9 +44,9 @@ export default function WishesAccountInput({ // } if ( - watchAccount === accountData?.account && - watchName === accountData?.name && - watchBank === accountData?.bank + accountWatch === accountData?.account && + nameWatch === accountData?.name && + bankWatch === accountData?.bank ) { setBtnDisabled(true); return; @@ -286,9 +111,9 @@ export default function WishesAccountInput({ if (btnDisalbed) return; const accountInfo: AccountInfoType = { - account: watchAccount, - bank: watchBank, - name: watchName, + account: accountWatch, + bank: bankWatch, + name: nameWatch, }; postVerifyAccount(accountInfo); }; diff --git a/src/container/wishes/create/client.tsx b/src/container/wishes/create/client.tsx index b698072..4f5205f 100644 --- a/src/container/wishes/create/client.tsx +++ b/src/container/wishes/create/client.tsx @@ -6,10 +6,13 @@ import dynamic from 'next/dynamic'; import { WishesAccountDataType, WishesLinkDataType } from '@/types/input'; import { WishesCreateStepType } from '@/app/wishes/create/page'; import { UserAccountDataType } from '@/types/api/response'; -import { WishesLinkInputForm } from './createInputForm'; import { useForm } from 'react-hook-form'; +import { wishesAccountInputInit, wishesLinkInputInit } from '@/constant/init'; +import WishesLinkInputForm from './wishesLinkInputForm'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { wishesLinkDataResolver, WishesLinkResolverType } from '@/validation/wishes.validate'; -const WishesAccountInput = dynamic(() => import('./createInputForm')); +const WishesAccountInputForm = dynamic(() => import('./WishesAccountInputForm')); export default function WishesCreatePageStateContainer({ createStep, @@ -22,26 +25,19 @@ export default function WishesCreatePageStateContainer({ userAccountData: UserAccountDataType; children: ReactNode; }) { - const wishesLinkInputMethods = useForm({ + const wishesLinkInputMethods = useForm({ mode: 'onChange', defaultValues: { - imageUrl: '', + ...wishesLinkInputInit, title: wishTitle, - hint: '', - startDate: new Date(), - endDate: getDate(new Date(), 7), - wantsGift: false, }, + resolver: yupResolver(wishesLinkDataResolver), }); const wishesAccountInputMethods = useForm({ mode: 'onChange', defaultValues: { - name: '', - account: '', - bank: '', - phone: '', - noticeAgree: false, + ...wishesAccountInputInit, }, }); @@ -52,7 +48,7 @@ export default function WishesCreatePageStateContainer({ { link: , account: ( - import('./dropdownContent')); + +export default function WishesLinkInputForm({ + methods, +}: { + methods?: UseFormReturn; +}) { + const router = useRouter(); + const { toggleState: wantsGiftWatch, changeOpenState: changeWantsGiftWatchState } = useToggle(); + + function handleNextStep() { + const wishTitle = methods.getValues('title'); + + if (wantsGiftWatch) { + router.push(`/wishes/create?step=account&wishTitle=${wishTitle}`); + } else { + createOnlyLettersWishes(); + } + } + + function handleSetImageUrl(imageUrl: string) { + methods.setValue('imageUrl', imageUrl); + } + + function createOnlyLettersWishes() { + // try { + // postWishes(methods).then((response) => { + // if (response.data.success) { + // router.push('/wishes/share'); + // } + // }); + // } catch (error) {} + } + + function handleChangeDate(selectedDate: Date) { + methods.setValue('startDate', selectedDate); + methods.setValue('endDate', getDate(selectedDate, 7)); + } + + return ( + <> +
{})}> + + + + inputType="textarea" + inputTitle="친구에게 남기고 싶은 한마디" + register={methods.register('hint')} + placeholder="ex.) 생일을 축하합니다~" + maxLength={MAX_TEXTAREA_LENGTH} + /> + + +
+ {/* + */} +
+
+ + + + + + + + + ); +} + +function WishesLinkSubmitButton({ + wantsGiftWatch, + handleNextStep, + disabled, +}: { + wantsGiftWatch: boolean; + handleNextStep: () => void; + disabled: boolean; +}) { + return ( +
+ {wantsGiftWatch && ( + + )} + + +
+ ); +} + +const SelectWantsGiftOption = React.memo(function SelectWantsGiftOption({ + wantsGiftWatch, + changeWantsGiftWatchState, +}: { + wantsGiftWatch: boolean; + changeWantsGiftWatchState: (state: boolean) => void; +}) { + const { + toggleState: dropDownState, + handleToggle: handleDropBoxState, + changeOpenState: changeDropBoxState, + } = useToggle(); + + function handleChangeWantsGiftState(state: boolean) { + changeWantsGiftWatchState(state); + } + + return ( +
    +
  • handleChangeWantsGiftState(true)} + > + + + 네! 생일 선물도 받아볼래요 + + {dropDownState && } +
  • + +
  • { + handleChangeWantsGiftState(false); + changeDropBoxState(false); + }} + > + + 아니요. 편지만 받을래요! +
  • +
+ ); +}); + +function ImageToShowToGiver({ + handleSetImageUrl, +}: { + handleSetImageUrl: (imageUrl: string) => void; +}) { + const { imageUrl, uploadImageFile } = useUploadItemInfo(); + + useEffect(() => { + if (imageUrl) { + handleSetImageUrl(imageUrl); + } + }, [imageUrl]); + + return ( + + + + ); +} diff --git a/src/hooks/common/useWatchFormValueList.ts b/src/hooks/common/useWatchFormValueList.ts new file mode 100644 index 0000000..99a3f5c --- /dev/null +++ b/src/hooks/common/useWatchFormValueList.ts @@ -0,0 +1,22 @@ +import { Control, Path, useWatch } from 'react-hook-form'; + +type WatchObjectType = { + [K in keyof T as `${string & K}Watch`]: T[K]; +}; + +export function useWatchTest(control: Control): WatchObjectType { + const objectKeys = Object.keys(control._defaultValues) as Array>; // Path 타입으로 변환 + + const watchArrayValues = useWatch({ + control, + name: [...objectKeys], + }); + + const watchObject = objectKeys.reduce((acc, key, index) => { + const watchKey = `${key}Watch` as keyof WatchObjectType; + acc[watchKey] = watchArrayValues[index]; + return acc; + }, {} as WatchObjectType); + + return watchObject; +} diff --git a/src/hooks/wishes/useUploadItemInfo.ts b/src/hooks/wishes/useUploadItemInfo.ts index 4932d4f..961aff4 100644 --- a/src/hooks/wishes/useUploadItemInfo.ts +++ b/src/hooks/wishes/useUploadItemInfo.ts @@ -4,37 +4,36 @@ import { useEffect, useState } from 'react'; export function useUploadItemInfo() { const [imageFile, setImageFile] = useState(null); - const [imageURL, setImageURL] = useState(''); - const [signedUrl, setSignedUrl] = useState(''); + const [imageUrl, setImageUrl] = useState(''); + const [signedURL, setSignedURL] = useState(''); const [filename, setFilename] = useState(''); useEffect(() => { - if (!filename || !signedUrl) return; + if (!signedURL || !filename) return; try { - uploadPresignedURL(signedUrl, imageFile).then((signedResponse) => { + uploadPresignedURL(signedURL, imageFile).then((signedResponse) => { if (signedResponse.status) { const S3_URL = `${process.env.NEXT_PUBLIC_S3_URL}/${filename}`; - setImageURL(S3_URL); + setImageUrl(S3_URL); } }); } catch (error) {} - }, [filename, signedUrl]); + }, [signedURL, filename]); useEffect(() => { - if (!imageFile || !validation.checkImageFileSize(imageFile.size)) return; - - try { - getPresignedURL(imageFile.name).then((presignedResponse) => { - if (presignedResponse.status) { - const signedUrl = presignedResponse.data.data.signedUrl; - const filename = presignedResponse.data.data.filename; - - setSignedUrl(signedUrl); - setFilename(filename.substring(1)); - } - }); - } catch (error) {} + if (imageFile && validation.checkImageFileSize(imageFile.size)) { + try { + getPresignedURL(imageFile.name).then((presignedResponse) => { + if (presignedResponse.status) { + const signedURL = presignedResponse.data.data.signedUrl; + const filename = presignedResponse.data.data.filename; + setSignedURL(signedURL); + setFilename(filename); + } + }); + } catch (error) {} + } }, [imageFile]); function uploadImageFile(e: React.ChangeEvent) { @@ -42,5 +41,5 @@ export function useUploadItemInfo() { imageFile && setImageFile(imageFile); } - return { imageFile, imageURL, setImageURL, uploadImageFile }; + return { imageFile, imageUrl, setImageUrl, uploadImageFile }; } diff --git a/src/validation/wishes-register-options.ts b/src/validation/wishes-register-options.ts deleted file mode 100644 index 837e84a..0000000 --- a/src/validation/wishes-register-options.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { WishesAccountDataType, WishesLinkDataType } from '@/types/input'; -import { RegisterOptions } from 'react-hook-form'; - -export const wishesLinkDataValidate: Record = { - imageUrl: { - required: '필수 입력 항목입니다.', - }, - hint: { - required: '필수 입력 항목입니다.', - maxLength: { - value: 300, - message: '최대 300자 까지 입력이 가능합니다.', - }, - }, - title: {}, - startDate: {}, - endDate: {}, - wantsGift: {}, -}; - -export const wishesAccountDataValidate: Record = { - account: { - required: '계좌번호를 입력하세요.', - pattern: { - value: /^[0-9]+$/, - message: '숫자만 입력하세요.', - }, - minLength: { - value: 10, - message: '계좌번호는 최소 10자리여야 합니다.', - }, - maxLength: { - value: 15, - message: '계좌번호는 최대 15자리여야 합니다.', - }, - }, - name: { - required: '필수 입력 항목입니다.', - }, - bank: { - required: '필수 입력 항목입니다.', - }, - phone: { - required: '필수 입력 항목입니다.', - pattern: { - value: /^(01[016789]{1})?[0-9]{3,4}?[0-9]{4}$/, - message: '핸드폰 형식에 맞게 입력해주세요', - }, - minLength: { - value: 10, - message: '휴대폰 번호는 최소 10자리여야 합니다.', - }, - maxLength: { - value: 11, - message: '휴대폰 번호는 최대 11자리여야 합니다.', - }, - }, - noticeAgree: {}, -}; diff --git a/src/validation/wishes.validate.ts b/src/validation/wishes.validate.ts new file mode 100644 index 0000000..7a2af29 --- /dev/null +++ b/src/validation/wishes.validate.ts @@ -0,0 +1,40 @@ +import * as yup from 'yup'; + +export const wishesLinkDataResolver = yup + .object() + .shape({ + imageUrl: yup.string().required('Image URL is required'), + title: yup.string().required('Title is required'), + hint: yup.string().required('Hint is required'), + startDate: yup.date().required('Start date is required'), + endDate: yup.date().required('End date is required'), + wantsGift: yup.boolean().required('Wants gift is required'), + }) + .noUnknown(true, 'Unknown field is not allowed') + .required(); + +export type WishesLinkResolverType = yup.InferType; + +export const wishesAccountDataResolver = yup.object().shape({ + account: yup + .string() + .matches(/^[0-9]+$/, '숫자만 입력하세요.') + .min(10, '계좌번호는 최소 10자리여야 합니다.') + .max(15, '계좌번호는 최대 15자리여야 합니다.') + .required('계좌번호를 입력하세요.'), + + name: yup.string().required('필수 입력 항목입니다.'), + + bank: yup.string().required('필수 입력 항목입니다.'), + + phone: yup + .string() + .matches(/^(01[016789]{1})?[0-9]{3,4}?[0-9]{4}$/, '핸드폰 형식에 맞게 입력해주세요') + .min(10, '휴대폰 번호는 최소 10자리여야 합니다.') + .max(11, '휴대폰 번호는 최대 11자리여야 합니다.') + .required('필수 입력 항목입니다.'), + + noticeAgree: yup.boolean().required(), +}); + +export type WishesAccountDataResolverType = yup.InferType; diff --git a/yarn.lock b/yarn.lock index 6bef0ad..cfd6834 100644 --- a/yarn.lock +++ b/yarn.lock @@ -613,6 +613,15 @@ __metadata: languageName: node linkType: hard +"@hookform/resolvers@npm:^3.9.0": + version: 3.9.0 + resolution: "@hookform/resolvers@npm:3.9.0" + peerDependencies: + react-hook-form: ^7.0.0 + checksum: 10c0/0e0e55f63abbd212cf14abbd39afad1f9b6105d6b25ce827fc651b624ed2be467ebe9b186026e0f032062db59ce2370b14e9583b436ae2d057738bdd6f04356c + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.14": version: 0.11.14 resolution: "@humanwhocodes/config-array@npm:0.11.14" @@ -6184,6 +6193,13 @@ __metadata: languageName: node linkType: hard +"property-expr@npm:^2.0.5": + version: 2.0.6 + resolution: "property-expr@npm:2.0.6" + checksum: 10c0/69b7da15038a1146d6447c69c445306f66a33c425271235bb20507f1846dbf9577a8f9dfafe8acbfcb66f924b270157f155248308f026a68758f35fc72265b3c + languageName: node + linkType: hard + "proxy-from-env@npm:^1.1.0": version: 1.1.0 resolution: "proxy-from-env@npm:1.1.0" @@ -7179,6 +7195,13 @@ __metadata: languageName: node linkType: hard +"tiny-case@npm:^1.0.3": + version: 1.0.3 + resolution: "tiny-case@npm:1.0.3" + checksum: 10c0/c0cbed35884a322265e2cd61ff435168d1ea523f88bf3864ce14a238ae9169e732649776964283a66e4eb882e655992081d4daf8c865042e2233425866111b35 + languageName: node + linkType: hard + "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -7202,6 +7225,13 @@ __metadata: languageName: node linkType: hard +"toposort@npm:^2.0.2": + version: 2.0.2 + resolution: "toposort@npm:2.0.2" + checksum: 10c0/ab9ca91fce4b972ccae9e2f539d755bf799a0c7eb60da07fd985fce0f14c159ed1e92305ff55697693b5bc13e300f5417db90e2593b127d421c9f6c440950222 + languageName: node + linkType: hard + "totalist@npm:^1.0.0": version: 1.1.0 resolution: "totalist@npm:1.1.0" @@ -7370,6 +7400,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^2.19.0": + version: 2.19.0 + resolution: "type-fest@npm:2.19.0" + checksum: 10c0/a5a7ecf2e654251613218c215c7493574594951c08e52ab9881c9df6a6da0aeca7528c213c622bc374b4e0cb5c443aa3ab758da4e3c959783ce884c3194e12cb + languageName: node + linkType: hard + "typed-array-buffer@npm:^1.0.2": version: 1.0.2 resolution: "typed-array-buffer@npm:1.0.2" @@ -7558,6 +7595,7 @@ __metadata: version: 0.0.0-use.local resolution: "version14@workspace:." dependencies: + "@hookform/resolvers": "npm:^3.9.0" "@next/bundle-analyzer": "npm:^13.4.19" "@nextui-org/skeleton": "npm:^2.0.32" "@testing-library/dom": "npm:^10.4.0" @@ -7589,6 +7627,7 @@ __metadata: ts-jest: "npm:^29.2.4" ts-node: "npm:^10.9.2" typescript: "npm:^5" + yup: "npm:^1.4.0" languageName: unknown linkType: soft @@ -7897,3 +7936,15 @@ __metadata: checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f languageName: node linkType: hard + +"yup@npm:^1.4.0": + version: 1.4.0 + resolution: "yup@npm:1.4.0" + dependencies: + property-expr: "npm:^2.0.5" + tiny-case: "npm:^1.0.3" + toposort: "npm:^2.0.2" + type-fest: "npm:^2.19.0" + checksum: 10c0/fe142141365eed0f78fb2e18bdd2f10bf101385dae12a5f9de14884448067bdca16a54b547fc0bffec04a098dd70b4519ff366422f3da006fd11a0717a7863ac + languageName: node + linkType: hard