From fe85046dc4b8cf574f8cf9412d136202e003b37a Mon Sep 17 00:00:00 2001 From: heejin Date: Fri, 30 May 2025 20:52:59 +0900 Subject: [PATCH 1/5] Chore : install react-hook-form & zod --- package-lock.json | 48 ++++++++++++++++++++++++++++++++++++++++++++++- package.json | 5 ++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 17295683..39c6eae2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", + "@hookform/resolvers": "^5.0.1", "axios": "^1.9.0", "clsx": "^2.1.1", "date-fns": "^4.1.0", @@ -19,8 +20,10 @@ "react-calendar": "^5.1.0", "react-dom": "^19.0.0", "react-error-boundary": "^6.0.0", + "react-hook-form": "^7.56.4", "react-intersection-observer": "^9.16.0", - "react-toastify": "^11.0.5" + "react-toastify": "^11.0.5", + "zod": "^3.25.41" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -2048,6 +2051,18 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@hookform/resolvers": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.0.1.tgz", + "integrity": "sha512-u/+Jp83luQNx9AdyW2fIPGY6Y7NG68eN2ZW8FOJYL+M0i4s49+refdJdOp/A9n9HFQtQs3HIDHQvX3ZET2o7YA==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.55.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -2774,6 +2789,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", @@ -8181,6 +8202,22 @@ "react": ">=16.13.1" } }, + "node_modules/react-hook-form": { + "version": "7.56.4", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.56.4.tgz", + "integrity": "sha512-Rob7Ftz2vyZ/ZGsQZPaRdIefkgOSrQSPXfqBdvOPwJfoGnjwRJUs7EM7Kc1mcoDv3NOtqBzPGbcMB8CGn9CKgw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-intersection-observer": { "version": "9.16.0", "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.16.0.tgz", @@ -9718,6 +9755,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.25.41", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.41.tgz", + "integrity": "sha512-8+sDJTGtCYIDBhdqDygp0ffj8kzziRKqAJPhpYObbElJ+3TRe/mnlnwH+/OMa3kKhueS4Drm5UMW00/u1p07zA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index fe185f42..53b9ef08 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", + "@hookform/resolvers": "^5.0.1", "axios": "^1.9.0", "clsx": "^2.1.1", "date-fns": "^4.1.0", @@ -20,8 +21,10 @@ "react-calendar": "^5.1.0", "react-dom": "^19.0.0", "react-error-boundary": "^6.0.0", + "react-hook-form": "^7.56.4", "react-intersection-observer": "^9.16.0", - "react-toastify": "^11.0.5" + "react-toastify": "^11.0.5", + "zod": "^3.25.41" }, "devDependencies": { "@eslint/eslintrc": "^3", From 6b4995f6a1d25f7aef9a3c43e5a92edc686404d0 Mon Sep 17 00:00:00 2001 From: heejin Date: Sun, 1 Jun 2025 20:52:14 +0900 Subject: [PATCH 2/5] Refactor : new form field --- src/components/common/formField/Fields.tsx | 32 +++++++++++++++++++ .../common/formField/compound/Input.tsx | 3 +- src/components/common/formField/style.ts | 1 + src/components/common/formField/type.ts | 19 +++++++++-- 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 src/components/common/formField/Fields.tsx diff --git a/src/components/common/formField/Fields.tsx b/src/components/common/formField/Fields.tsx new file mode 100644 index 00000000..94b82820 --- /dev/null +++ b/src/components/common/formField/Fields.tsx @@ -0,0 +1,32 @@ +'use client'; + +import clsx from 'clsx'; +import { GAP_SIZE, LABEL_SIZE } from './style'; +import { FieldProps } from './type'; + +export default function Fields({ + label, + required, + errorMessage, + gapSize = '12', + labelSize = '16/16', + render, +}: FieldProps) { + const showError = !!errorMessage; + + return ( +
+
+ {label && ( + + )} + {render()} +
+ + {showError && {errorMessage}} +
+ ); +} diff --git a/src/components/common/formField/compound/Input.tsx b/src/components/common/formField/compound/Input.tsx index 7803517a..631f6cee 100644 --- a/src/components/common/formField/compound/Input.tsx +++ b/src/components/common/formField/compound/Input.tsx @@ -7,6 +7,7 @@ import { InputProps } from '../type'; export default function Input({ leftSlot = null, rightSlot = null, + hasError, borderClassName = 'border-border', className, ref, @@ -16,7 +17,7 @@ export default function Input({
diff --git a/src/components/common/formField/style.ts b/src/components/common/formField/style.ts index 5a285b87..4b5b3aa2 100644 --- a/src/components/common/formField/style.ts +++ b/src/components/common/formField/style.ts @@ -14,6 +14,7 @@ export const LABEL_SIZE = { '16/20': 'text-lg-md sm:text-xl-bold', } as const; +// 삭제 예정 export const getBorderClassName = ({ isFocused, showSuccess, diff --git a/src/components/common/formField/type.ts b/src/components/common/formField/type.ts index 9118aadc..32d44d27 100644 --- a/src/components/common/formField/type.ts +++ b/src/components/common/formField/type.ts @@ -1,8 +1,10 @@ import { InputHTMLAttributes, TextareaHTMLAttributes } from 'react'; +import { GAP_SIZE, LABEL_SIZE } from './style'; export interface InputProps extends InputHTMLAttributes { leftSlot?: React.ReactNode; rightSlot?: React.ReactNode; + hasError?: boolean; borderClassName?: string; className?: string; ref?: React.Ref; @@ -15,14 +17,17 @@ export interface TextareaProps extends TextareaHTMLAttributes { + // imageUploaderType 삭제 예정 imageUploaderType?: ImageUploaderType; image: string | null; onImageChange: (e: React.ChangeEvent) => void; } +// 삭제 예정 export interface FormFieldProps { field: 'input' | 'textarea' | 'file-input'; imageUploaderType?: ImageUploaderType; @@ -34,10 +39,20 @@ export interface FormFieldProps { errorMessage?: string; gapSize?: '12' | '16' | '24' | '32'; labelSize?: '16/16' | '14/16' | '16/20'; - onFieldFocus?: () => void; - onFieldBlur?: () => void; + onFieldFocus?: (e: React.FocusEvent) => void; + onFieldBlur?: (e: React.FocusEvent) => void; } +export interface FieldProps { + label?: string; + required?: boolean; + errorMessage?: string; + gapSize?: keyof typeof GAP_SIZE; + labelSize?: keyof typeof LABEL_SIZE; + render: () => React.ReactNode; +} + +// 삭제 에정 export type FieldComponentProps = | (InputProps & FormFieldProps & { field?: 'input' }) | (TextareaProps & FormFieldProps & { field?: 'textarea' }) From 0fd2311715fc9a1039b4c2fc7cbe0c9d9257a8e5 Mon Sep 17 00:00:00 2001 From: heejin Date: Mon, 2 Jun 2025 03:35:38 +0900 Subject: [PATCH 3/5] Refactor : useZodForm --- src/hooks/useZodForm.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/hooks/useZodForm.ts diff --git a/src/hooks/useZodForm.ts b/src/hooks/useZodForm.ts new file mode 100644 index 00000000..f9770ed1 --- /dev/null +++ b/src/hooks/useZodForm.ts @@ -0,0 +1,19 @@ +import { useForm, UseFormProps, UseFormReturn } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { ZodSchema } from 'zod'; + +export default function useZodForm>({ + validationSchema, + defaultValues, + ...rest +}: { + validationSchema: ZodSchema; + defaultValues: UseFormProps['defaultValues']; +} & Omit, 'resolver' | 'defaultValues'>): UseFormReturn { + return useForm({ + resolver: zodResolver(validationSchema), + defaultValues, + mode: 'all', + ...rest, + }); +} From 9336cbe7bad97b1ce144e4d500a1cabf99e33bb9 Mon Sep 17 00:00:00 2001 From: heejin Date: Mon, 2 Jun 2025 03:57:34 +0900 Subject: [PATCH 4/5] Refactor : image uploader object-cover --- src/components/common/formField/compound/ImageUploader.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/common/formField/compound/ImageUploader.tsx b/src/components/common/formField/compound/ImageUploader.tsx index 30691c3f..4fef0de7 100644 --- a/src/components/common/formField/compound/ImageUploader.tsx +++ b/src/components/common/formField/compound/ImageUploader.tsx @@ -41,12 +41,12 @@ export default function ImageUploader({ imageUploaderType, image, inputRef }: Im