์ด ๊ฒ์๋ฌผ์์๋ ๋ฆฌ์กํธ์์ ํผ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ๊ตฌํํ๋ ์ฌ๋ฌ ๋ฐฉ๋ฒ์ ์๊ฐํฉ๋๋ค. JavaScript๋ฅผ ์ด์ฉํ ๊ธฐ๋ณธ ๊ฒ์ฆ ๋ฐฉ์๋ถํฐ, Yup, Formik, ๊ทธ๋ฆฌ๊ณ ์ฑ๋ฅ ์ต์ ํ์ ๊ฐ์ ์ด ์๋ react-hook-form๊น์ง ๋น๊ตํ์ฌ ์ค๋ช ํ๊ฒ ์ต๋๋ค.๐
1๏ธโฃ JavaScript๋ก ์ง์ ๊ตฌํํ๋ ๊ฒฝ์ฐ
๋ฆฌ์กํธ์์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ์์ผ๋ก, ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ JavaScript๋ก ์ง์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
import { useState } from 'react';
function MyForm() {
const [name, setName] = useState('');โจ
const [age, setAge] = useState('');โจ
const [errors, setErrors] = useState({});โจ
const handleSubmit = (e) => {โจ
e.preventDefault();
const newErrors = {};
if (!name) newErrors.name = '์ด๋ฆ์ ์
๋ ฅํ์ธ์';
if (!age) newErrors.age = '๋์ด๋ฅผ ์
๋ ฅํ์ธ์';
else if (age <= 0) newErrors.age = '๋์ด๋ 0๋ณด๋ค ์ปค์ผ ํฉ๋๋ค';
setErrors(newErrors);
if (Object.keys(newErrors).length === 0) {
// ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ํต๊ณผ๋ ๊ฒฝ์ฐ ์ฒ๋ฆฌ ๋ก์ง
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>์ด๋ฆโจ</label>
<input type="text" value={name} onChange={(e) => setName(e.target.value)} />
{errors.name && <div className="error">{errors.name}</div>}
</div>
<div>
<label>๋์ดโจ</label>
<input type="number" value={age} onChange={(e) => setAge(e.target.value)} />
{errors.age && <div className="error">{errors.age}</div>}
</div>
<button type="submit">์ ์ถโจ</button>
</form>
);
}
์ฅ์
- ๊ฐ๋จํ ๋ก์ง์ ์ง์ ๊ตฌํ ๊ฐ๋ฅ.
- ๋ณ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด ๋ฐ๋ก ๊ฒ์ฆ ๋ก์ง ์์ฑ ๊ฐ๋ฅ.
๋จ์
- ๊ฒ์ฆํ ํญ๋ชฉ์ด ๋ง์์ง์๋ก ์ฝ๋๊ฐ ๊ธธ์ด์ง๊ณ ๋ณต์กํด์ง ์ ์์.
- ์ค๋ณต๋ ๊ฒ์ฆ ๋ก์ง์ ์ฌ๋ฌ ๊ณณ์์ ์์ฑํ ์ ์์.
2๏ธโฃ Yup๋ง ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
Yup์ ํผ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ํ ์คํค๋ง ๊ธฐ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. Formik ์์ด๋ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ๋ค์ํ ๊ท์น์ ๊ฐ๊ฒฐํ๊ฒ ์ ์ธํ ์ ์์ต๋๋ค.
import { useState } from 'react';
import * as Yup from 'yup';
function MyForm() {
const [formData, setFormData] = useState({ name: '', age: '' });โจ
const [errors, setErrors] = useState({});
// Yup ์คํค๋ง ์ ์โจ
const validationSchema = Yup.object().shape({
name: Yup.string().required('์ด๋ฆ์ ์
๋ ฅํ์ธ์'),
age: Yup.number()
.required('๋์ด๋ฅผ ์
๋ ฅํ์ธ์')
.positive('๋์ด๋ 0๋ณด๋ค ์ปค์ผ ํฉ๋๋ค')
.integer('๋์ด๋ ์ ์์ฌ์ผ ํฉ๋๋ค'),
});
const handleSubmit = async (e) => {
e.preventDefault();
try {
await validationSchema.validate(formData, { abortEarly: false });โจ
setErrors({});
// ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํต๊ณผํ ๊ฒฝ์ฐ ์ฒ๋ฆฌ ๋ก์ง
} catch (err) {
setErrors(err.inner.reduce((acc, error) => ({ ...acc, [error.path]: error.message }), {}));
}
};
const handleChange = (e) => setFormData({ ...formData, [e.target.name]: e.target.value });
return (
<form onSubmit={handleSubmit}>
<div>
<label>์ด๋ฆ</label>
<input type="text" name="name" value={formData.name} onChange={handleChange} />
{errors.name && <div className="error">{errors.name}</div>}
</div>
<div>
<label>๋์ด</label>
<input type="number" name="age" value={formData.age} onChange={handleChange} />
{errors.age && <div className="error">{errors.age}</div>}
</div>
<button type="submit">์ ์ถ</button>
</form>
);
}
export default MyForm;
์ฅ์
- ๋ณต์กํ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌํ ์ ์์.
- Yup์ ๋ค์ํ ๊ฒ์ฆ ๊ท์น์ ํ์ฉ ๊ฐ๋ฅ.
- Formik ์์ด๋ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ฝ๊ฒ ๊ตฌํ ๊ฐ๋ฅ.
๋จ์
- ํผ ์ํ๋ฅผ ์ง์ ๊ด๋ฆฌํด์ผ ํจ.
- Formik์ด ์ ๊ณตํ๋ ํผ ๊ด๋ฆฌ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์.
3๏ธโฃ Formik๋ง ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
Formik์ ํผ ๊ด๋ฆฌ๋ฅผ ๊ฐํธํ๊ฒ ํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. validate ์์ฑ์ ์ฌ์ฉํ์ฌ ํผ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํ ์ ์์ต๋๋ค.
import { Formik } from 'formik';
<Formikโจ
initialValues={{ name: '', age: '' }}
validate={values => {
const errors = {};
if (!values.name) errors.name = '์ด๋ฆ์ ์
๋ ฅํ์ธ์';
if (!values.age) errors.age = '๋์ด๋ฅผ ์
๋ ฅํ์ธ์';
else if (values.age <= 0) errors.age = '๋์ด๋ 0๋ณด๋ค ์ปค์ผ ํฉ๋๋ค';
return errors;
}}
onSubmit={(values) => {
// ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ํต๊ณผ๋ ๊ฒฝ์ฐ ์ฒ๋ฆฌ ๋ก์ง
}}
>
{({ handleChange, handleSubmit, values, errors }) => (โจ
<form onSubmit={handleSubmit}>
<div>
<label>์ด๋ฆ</label>
<input type="text" name="name" value={values.name} onChange={handleChange} />
{errors.name && <div className="error">{errors.name}</div>}
</div>
<div>
<label>๋์ด</label>
<input type="number" name="age" value={values.age} onChange={handleChange} />
{errors.age && <div className="error">{errors.age}</div>}
</div>
<button type="submit">์ ์ถ</button>
</form>
)}
</Formik>
์ฅ์
- ํผ ๊ด๋ฆฌ์ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํ ๊ณณ์์ ์ฒ๋ฆฌ ๊ฐ๋ฅ.
- ๊ฐ๋จํ ๊ฒ์ฆ ๋ก์ง์ Formik ๋ด์์ ์ฒ๋ฆฌํ ์ ์์.
๋จ์
- ๋ณต์กํ ๊ฒ์ฆ ๋ก์ง์ ๊ตฌํํ ๋ ์ฝ๋๊ฐ ๊ธธ์ด์ง ์ ์์.
4๏ธโฃ Formik + Yup์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ( ๋ณต์กํ ์ ํจ์ฑ ๊ฒ์ฌ ๐๐ป)
Formik๊ณผ Yup์ ํจ๊ป ์ฌ์ฉํ๋ฉด ํผ ๊ด๋ฆฌ๋ Formik์ด, ์ ํจ์ฑ ๊ฒ์ฌ๋ Yup์ด ๋ด๋นํฉ๋๋ค. ์ด ๋ฐฉ์์ ๋ณต์กํ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ๊ฐ๊ฒฐํ๊ฒ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
import { Formik } from 'formik';
import * as Yup from 'yup';
const validationSchema = Yup.object({โจ
name: Yup.string().required('์ด๋ฆ์ ์
๋ ฅํ์ธ์'),
age: Yup.number().required('๋์ด๋ฅผ ์
๋ ฅํ์ธ์').positive('๋์ด๋ 0๋ณด๋ค ์ปค์ผ ํฉ๋๋ค').integer('๋์ด๋ ์ ์์ฌ์ผ ํฉ๋๋ค'),
});
<Formikโจ
initialValues={{ name: '', age: '' }}
validationSchema={validationSchema}
onSubmit={(values) => {
// ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ํต๊ณผ๋ ๊ฒฝ์ฐ ์ฒ๋ฆฌ ๋ก์ง
}}
>
{({ handleChange, handleSubmit, values, errors }) => (
<form onSubmit={handleSubmit}>โจ
<div>
<label>์ด๋ฆ</label>
<input type="text" name="name" value={values.name} onChange={handleChange} />
{errors.name && <div className="error">{errors.name}</div>}
</div>
<div>
<label>๋์ด</label>
<input type="number" name="age" value={values.age} onChange={handleChange} />
{errors.age && <div className="error">{errors.age}</div>}
</div>
<button type="submit">์ ์ถ</button>
</form>
)}
</Formik>
์ฅ์
- ํผ ๊ด๋ฆฌ์ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ๊ฐ๊ฐ Formik๊ณผ Yup์์ ๋ด๋น.
- ๊ฐ๊ฒฐํ ์ฝ๋๋ก ๋ณต์กํ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ฒ๋ฆฌํ ์ ์์.
- ๊ฒ์ฆ ์คํค๋ง๋ฅผ ์ฌ์ฌ์ฉํ ์ ์์.
๋จ์
- Yup์ ์ถ๊ฐ๋ก ์ค์นํด์ผ ํจ.
- ๋งค์ฐ ๊ฐ๋จํ ํผ์ผ ๊ฒฝ์ฐ, ๋ถํ์ํ๊ฒ ๋๊ปด์ง ์ ์์.
์ฝ๋ ๋น๊ตํด๋ณด๊ธฐ(JavaScript๋ก ์ง์ ๊ตฌํํ๋ ๊ฒฝ์ฐ์ ๋น๊ต)
5๏ธโฃ react-hook-form์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ( ๊ฐ๋จํ ์ ํจ์ฑ ๊ฒ์ฌ ๐๐ป)
react-hook-form์ ๋ฆฌ์กํธ์์ ๊ฐ๋จํ๊ณ ์ฑ๋ฅ ์ข์ ํผ ๊ด๋ฆฌ ๋ฐ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ ๊ณตํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. useForm ํ ์ ํตํด ํผ์ ์ํ๋ฅผ ์ด๊ธฐํํ๊ณ , ํ๋์ ์ฐ๊ฒฐ๋ ์ํ๋ฅผ ์๋์ผ๋ก ๊ด๋ฆฌํ ์ ์์ผ๋ฉฐ, register ํจ์๋ก ์ ๋ ฅ ํ๋๋ฅผ ํผ์ ์ฝ๊ฒ ๋ฑ๋กํ ์ ์์ต๋๋ค. ๋ํ, watch๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ํ๋ ๊ฐ์ ์ค์๊ฐ์ผ๋ก ๋ชจ๋ํฐ๋งํ๊ฑฐ๋, ์ ํจ์ฑ ๊ฒ์ฌ ๊ท์น์ ์ถ๊ฐํ์ฌ ๋ค์ํ ํผ ๊ฒ์ฆ์ ๊ฐํธํ๊ฒ ์ํํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ๋ฆฌ์กํธ์์ ํผ ์ฒ๋ฆฌ ๋ก์ง์ ๊ฐ๊ฒฐํ๊ฒ ์ ์งํ๋ฉด์๋ ์ฑ๋ฅ์ ์ต์ ํํ ์ ์์ต๋๋ค.
* Formik์ Yup๊ณผ ํจ๊ป ์ฐ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๊ณ , react-hook-form์ ๊ฐ๋จํ๊ฒ ์ฐ์ด๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ Yup๊ณผ ํจ๊ป ์ฐ๋ ๊ฒฝ์ฐ๊ฐ ์ ์ต๋๋ค.
import { useForm } from 'react-hook-form';
function MyForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm(); // โจ useForm ํ
์ ์ฌ์ฉํ์ฌ ํผ์ ๊ด๋ฆฌ
const onSubmit = (data) => {
// ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ํต๊ณผ๋ ๊ฒฝ์ฐ ์ฒ๋ฆฌ ๋ก์ง
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label>์ด๋ฆ</label>
<input {...register('name', { required: '์ด๋ฆ์ ์
๋ ฅํ์ธ์' })} />โจ
{errors.name && <div className="error">{errors.name.message}</div>}
</div>
<div>
<label>๋์ด</label>
<input
type="number"
{...register('age', {
required: '๋์ด๋ฅผ ์
๋ ฅํ์ธ์',
min: { value: 1, message: '๋์ด๋ 0๋ณด๋ค ์ปค์ผ ํฉ๋๋ค' },
})}
/>
{errors.age && <div className="error">{errors.age.message}</div>}
</div>
<button type="submit">์ ์ถ</button>
</form>
);
}