์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- JS
- ์ฝ์ฝ์ํก ํด๋ก ์ฝ๋ฉ
- ์ฝ๋ฉ๋ ํ
- typescript
- clone coding
- ๋ ธ๋ง๋ ์ฝ๋
- tailwindcss
- canvas image
- queryprovider
- ์น ๋ธ๋ผ์ฐ์ ๊ตฌ์กฐ
- canvas
- jest
- HTML
- tanstack-query
- ์ฝ๋ฉ์ผ๊ธฐ
- negative margin
- canvas js
- ์น ๋ธ๋ผ์ฐ์ ๋ ๋๋ง
- react
- CSS
- next.js
- ์ํ์ฝ๋ฉ
- ๋ ธ๋ง๋์ฝ๋
- ๋ฐ๋๋ผ JS๋ก ํฌ๋กฌ ์ฑ ๋ง๋ค๊ธฐ
- javascript
- ๋ฐ๋๋ผ JS๋ก ๊ทธ๋ฆผํ ๋ง๋ค๊ธฐ
- axios-mock-adapte
- ํ๋ก ํธ์๋
- JAVA Script
- HTML ํ์ผ๊ตฌ์กฐ
- Today
- Total
Coding Archive
[React Hook Form] Next.js ํ๋ก์ ํธ์ React Hook Form ์ ์ฉ๊ธฐ ๋ณธ๋ฌธ
[React Hook Form] Next.js ํ๋ก์ ํธ์ React Hook Form ์ ์ฉ๊ธฐ
์ฝ๋ฑ์ด 2024. 10. 15. 02:271. React Hook Form์ด๋?
react-hook-form์ ๊ฐ๋จํ๊ณ ํจ์จ์ ์ธ ๋ฐฉ์์ผ๋ก ํผ ์ ํจ์ฑ ๊ฒ์ฌ์ ์ํ ๊ด๋ฆฌ๋ฅผ ์ฒ๋ฆฌํ ์ ์๊ฒ ๋์์ฃผ๋ React ๊ธฐ๋ฐ์ ํผ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค.
1.1 ํ๋ก์ ํธ์์ React Hook Form์ ๋์ ํ ์ด์
react-hook-form์ ํ๋ก์ ํธ์ ๋์ ํ ๊ฐ์ฅ ํฐ ์ด์ ๋ '์ฑ๋ฅ'๊ณผ '์ฝ๋์ ๊ฐ๊ฒฐํจ' ๋๋ฌธ์ ๋๋ค. React์์ ํผ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ ๋๋ถ๋ถ useState๋ก ๊ฐ ํ๋์ ์ํ๋ฅผ ๊ด๋ฆฌํ๋๋ฐ, ์ด๋ ๋ณต์กํ ํผ์ผ์๋ก ์ฝ๋๊ฐ ๊ธธ์ด์ง๊ณ ์ํ ๋ณํ๋ง๋ค ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ์ฌ ์ฑ๋ฅ ์ ํ๋ก ์ด์ด์ง ์ ์์ต๋๋ค. ์ด์ ํ๋ก์ ํธ์์ useState ๊ธฐ๋ฐ์ผ๋ก ๋ชจ๋ ํ๋ ๊ฐ์ ์ํ๋ก ๊ด๋ฆฌํ๊ณ , ๊ฐ ํ๋๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ํผ ์ ์ฒด๊ฐ ๋ฆฌ๋ ๋๋ง๋๋ ๋นํจ์จ์ ์ธ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ ์์ด ์ด๋ฒ ํ๋ก์ ํธ์์ ์ด๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํด react-hook-form์ ๋์ ํ์ต๋๋ค.
react-hook-form์ ๋น์ ์ด ์ปดํฌ๋ํธ์ Ref ๊ธฐ๋ฐ์ผ๋ก ๋์ํ์ฌ ์ํ ๋ณํ๋ฅผ DOM์์ ์ง์ ๊ด๋ฆฌํ๊ธฐ ๋๋ฌธ์, ์ํ ๊ด๋ฆฌ์ ๋ฆฌ๋ ๋๋ง ๋ถ๋ด์ ์ค์ด๋ ๋์์ ์ฑ๋ฅ์ ์ต์ ํํ ์ ์์ต๋๋ค. ๋ํ, ์ ํจ์ฑ ๊ฒ์ฌ์ ํผ ์ํ ๊ด๋ฆฌ๋ฅผ ์ผ๊ด๋ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. register์ validation์ ํตํด ํ๋์ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ฝ๊ฒ ์ ์ํ ์ ์์ผ๋ฉฐ, ์ ์ถ ์ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํต๊ณผํ์ง ๋ชปํ ํ๋์ ๋ํด ์๋์ผ๋ก ์๋ฌ ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
๊ธฐ์กด ๋ฐฉ์:
- useState๋ก ๊ฐ ํ๋๋ง๋ค ์ํ ๊ด๋ฆฌ → ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง
- ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ํ ๋ณ๋์ ๋ก์ง, ๊ทธ์ ๋ฐ๋ฅธ ์ํ ๊ด๋ฆฌ์ ๋ณต์กํจ
React Hook Form ๋ฐฉ์:
- Ref๋ก ์ํ ๊ด๋ฆฌ → ๋ฆฌ๋ ๋๋ง ์ต์ํ, ์ฑ๋ฅ ์ต์ ํ
- ๊ธฐ๋ณธ์ ์ธ ์ ํจ์ฑ ๊ฒ์ฌ ์ถ๊ฐ ์ฉ์ด, ์ปค์คํ ์ ํจ์ฑ ๊ฒ์ฌ ์ ์ฐํ๊ฒ ์ฒ๋ฆฌ
2. ํ๋ก์ ํธ์ ์ ์ฉํ๊ธฐ
2.1 ํ๋ก์ ํธ ๊ตฌ์กฐ์ ๋ง์ถ ์ค์
ํ๋ก์ ํธ์์ ํผ์ ๊ตฌ์กฐํํ ๋, ๋จผ์ FormProvider๋ฅผ ํตํด react-hook-form์ ํผ ์ํ๋ฅผ ์ ์ญ์์ ๊ด๋ฆฌํ๋๋ก ์ค์ ํ์ต๋๋ค. react-hook-form์ FormProvider์ useForm ํ ์ ์ด์ฉํด ํผ ์ ์ฒด์ ์ํ๋ฅผ ์บก์ํํฉ๋๋ค. ํผ์ ๋ชจ๋ ์ํ๋ methods ๊ฐ์ฒด๋ก ์ ๊ณต๋๋ฉฐ, ์ด๋ฅผ ํตํด ๊ฐ ํ๋์ ์ํ์ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
const methods = useForm<ISignUpRequest>({
mode: 'onBlur', defaultValues: {
email: '',
password: '',
accountname: '',
username: '',
image: '',
intro: '',
},
});
FormProvider๋?
FormProvider๋ ํผ ์ํ๋ฅผ ์ ์ญ์ ์ผ๋ก ๊ด๋ฆฌํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ์ ๋๋ค. ์ฆ, ํผ ํ๋๋ค์ด ์ฌ๋ฌ ํ์ ์ปดํฌ๋ํธ๋ก ๋๋์ด ์๋๋ผ๋, ๊ฐ ์ปดํฌ๋ํธ์์ ๋์ผํ ํผ ์ํ๋ฅผ ๊ณต์ ํ๊ณ ๊ด๋ฆฌํ ์ ์๋๋ก ๋์์ค๋๋ค. ์ผ๋ฐ์ ์ผ๋ก ํผ์ด ํ๋์ ์ปดํฌ๋ํธ์์๋ง ๊ด๋ฆฌ๋๋ค๋ฉด ํ์ํ์ง ์์ง๋ง, ํผ ํ๋๊ฐ ์ฌ๋ฌ ์ปดํฌ๋ํธ์ ๋๋์ด์ ธ ์์ ๋๋ FormProvider๋ฅผ ์ฌ์ฉํ์ฌ ํผ ์ํ๋ฅผ ๊ณต์ ํ ์ ์์ต๋๋ค.
import { FormProvider, useForm } from 'react-hook-form';
export default function Signup() {
const methods = useForm<ISignUpRequest>();
return (
<FormProvider {...methods}> {/* FormProvider๋ก ๊ฐ์ธ์ ์ํ๋ฅผ ๊ณต์ */}
<form>
<AuthForm /> {/* AuthForm์์ useFormContext๋ก ํผ ์ํ๋ฅผ ์ ๊ทผ */}
<ProfileForm /> {/* ProfileForm์์๋ ๋์ผํ ํผ ์ํ ์ ๊ทผ */}
</form>
</FormProvider>
);
}
useFormContext๋?
useFormContext๋ FormProvider๋ก ๊ฐ์ธ์ง ์ปดํฌ๋ํธ์์ ํผ ์ํ๋ฅผ ์ ๊ทผํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ํ ์ ๋๋ค. ์ด ํ ์ ์ฌ์ฉํ๋ฉด ์ปดํฌ๋ํธ ๊ฐ์ ๋์ผํ ํผ ์ํ๋ฅผ ์ฝ๊ฒ ๊ณต์ ํ ์ ์์ต๋๋ค. ์๋์ ๊ฐ์ด ์ด๋ฉ์ผ, ๋น๋ฐ๋ฒํธ ์ ๋ ฅ ํ๋์ ํ๋กํ ์ ๋ ฅ ํ๋๊ฐ ์๋ก ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ์๋๋ผ๋, useFormContext๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ ํผ ์ํ์ ์ ๊ทผํ๊ณ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
import { useFormContext } from 'react-hook-form';
export default function AuthForm() {
const { register, formState: { errors } } = useFormContext(); // ํผ ์ํ ์ ๊ทผ
return (
<div>
<input {...register('email')} placeholder="์ด๋ฉ์ผ" />
{errors.email && <span>{errors.email.message}</span>}
<input {...register('password')} placeholder="๋น๋ฐ๋ฒํธ" />
{errors.password && <span>{errors.password.message}</span>}
</div>
);
}
import { useFormContext } from 'react-hook-form';
export default function ProfileForm() {
const { register, formState: { errors } } = useFormContext(); // ํผ ์ํ ์ ๊ทผ
return (
<div>
<input {...register('username')} placeholder="์ฌ์ฉ์ ์ด๋ฆ" />
{errors.username && <span>{errors.username.message}</span>}
</div>
);
}
2.2 ๊ธฐ๋ณธ ํผ ์ปดํฌ๋ํธ ๊ตฌํ๊ณผ ๋ฆฌํฉํ ๋ง
์ด๊ธฐ์ ์์ฑํ ํผ ์ปดํฌ๋ํธ๋ AuthForm๊ณผ ProfileForm์ผ๋ก ๋๋์ด ๊ฐ ํ๋๋ฅผ ๊ฐ๋ณ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ด์์ต๋๋ค. ๋์ผํ ๋ ์ด์์์ ๊ณต์ ํ์ง๋ง ProfileForm์์๋ userimage๋ฅผ ๋ฐ์์ผ ํ๊ธฐ ๋๋ฌธ์ ๊ฐ๊ฐ์ Form์ผ๋ก ๋๋์ด ๊ตฌํํ์์ต๋๋ค.
์ด๊ธฐ AuthForm ์ปดํฌ๋ํธ:
'use client';
import { useFormContext } from 'react-hook-form';
import { UnderlineInput } from '@/components/common/input/UnderlineInput';
interface AuthFormProps {
type: 'login' | 'signup';
}
const authFields = {
signup: {
email: {
label: '์ด๋ฉ์ผ',
placeholder: '์ด๋ฉ์ผ์ ์ค์ ํด์ฃผ์ธ์.',
},
password: {
label: '๋น๋ฐ๋ฒํธ',
placeholder: '๋น๋ฐ๋ฒํธ๋ฅผ ์ค์ ํด์ฃผ์ธ์.',
},
},
login: {
email: {
label: '์ด๋ฉ์ผ',
placeholder: '์ด๋ฉ์ผ์ ์
๋ ฅํ์ธ์.',
},
password: {
label: '๋น๋ฐ๋ฒํธ',
placeholder: '๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํ์ธ์.',
},
},
};
export default function AuthForm({ type }: AuthFormProps) {
const {
register,
formState: { errors },
} = useFormContext();
const fields = authFields[type];
return (
<fieldset className="flex-center gap-4">
{Object.entries(fields).map(([name, field]) => (
<UnderlineInput
key={name}
{...register(name)}
isClearable
variant="underlined"
type={name}
label={field.label}
placeholder={field.placeholder}
isInvalid={!!errors[name]}
errorMessage={errors[name]?.message?.toString()}
/>
))}
</fieldset>
);
}
๋์ผํ ๋ ์ด์์์ผ๋ก ์ค๋ณต ์ฝ๋๊ฐ ๋ง์๊ธฐ ๋๋ฌธ์, AuthForm๊ณผ ProfileForm์ ํ๋์ FormTemplate ์ปดํฌ๋ํธ๋ก ๋ฆฌํฉํ ๋งํ์ฌ ํผ ํ๋๋ฅผ ๋์ ์ผ๋ก ๊ด๋ฆฌํ๋๋ก ๋ณ๊ฒฝํ์ต๋๋ค.
๋ฆฌํฉํ ๋ง ํ FormTemplate ์ปดํฌ๋ํธ:
'use client';
import { useFormContext } from 'react-hook-form';
import { UnderlineInput } from '@/components/common/input/UnderlineInput';
import { IFields } from '@/config/authFieldConfig';
interface FormTemplateProps {
fields: IFields;
children?: React.ReactNode; // ์ถ๊ฐ์ ์ธ ์ปดํฌ๋ํธ(ํ๋กํ ์ฌ์ง) ๋ฐ์ ์ ์์
}
export default function FormTemplate({ fields, children }: FormTemplateProps) {
const {
register,
formState: { errors},
} = useFormContext();
return (
<fieldset className="flex-center gap-4">
{children}
{Object.entries(fields).map(([name, field]) => {
<UnderlineInput
key={name}
{...register(name, field.validation)}
isClearable
variant="underlined"
type={
name === 'email'
? 'email'
: name === 'password'
? 'password'
: 'text'
}
label={field.label}
placeholder={field.placeholder}
isInvalid={!!hasError}
errorMessage={errors[name]?.message?.toString()}
/>
})}
</fieldset>
);
}
๋ถ๋ชจ ์ปดํฌ๋ํธ๋ก๋ถํฐ fields(์: SIGN_UP_FIELDS, PROFILE_FIELDS, LOGIN_FIELDS)์ children์ props๋ก ๋ฐ์ ํผ์ด ๊ตฌ์ฑ๋๋๋ก ์์ ํ์ต๋๋ค. ์ด ๋๋ถ์ ํผ ํ๋์ ๋ํด ๋ ๊ฐ๊ฒฐํ๊ฒ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์์ผ๋ฉฐ, ์ ์ง๋ณด์์ฑ์ด ํฅ์๋์์ต๋๋ค.
3. ํธ๋ฌ๋ธ์ํ : ์ค๋ณต๋ ์ ํจ์ฑ ๊ฒ์ฌ
ํ์๊ฐ์ ์ ์คํ ๊ธฐ๋ฐ ํผ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ๋ฉด์, ๋ค์ ์คํ ์ผ๋ก ๋์ด๊ฐ ๋๋ง๋ค ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ํธ๋ฆฌ๊ฑฐ๋์ด ๋ ๋๋ง๋๋ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
์๋ฌ๊ฐ ๋ฐ์ํ๋ ์ด์ ๋ react-hook-form์ ๊ธฐ๋ณธ ๋์ ๋ฐฉ์ ๋๋ฌธ์ด์์ต๋๋ค. ํผ ํ๋๊ฐ ๋ ๋๋ง๋๋ ์๊ฐ, ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ์ฆ์ ์คํ๋์ด, ์คํ ์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ ๋ ฅ๊ฐ์ด ์์์๋ ๋ถ๊ตฌํ๊ณ ๋ฐ๋ก ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ด ๋ฌธ์ ์์ต๋๋ค.
ํด๊ฒฐ ๋ฐฉ๋ฒ
- clearErrors() ์ฌ์ฉ(ํด๊ฒฐX) : ์คํ ๋ณ๊ฒฝ ์ ์ด์ ์ ์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ๋ฅผ ์ด๊ธฐํํ๊ธฐ ์ํด clearErrors()๋ฅผ ์ฌ์ฉํ์ง๋ง, ์ฌ์ ํ ํ๋กํ ํ๋๊ฐ ๋ ๋๋ง๋ ๋ ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ๋ฐ์ํ์ต๋๋ค.
- touchedFields ํ์ฉ(ํด๊ฒฐO) : FormTemplate ์ปดํฌ๋ํธ์ touchedFields๋ฅผ ์ถ๊ฐํ์ฌ, ํ๋๊ฐ ํฐ์น๋ ๊ฒฝ์ฐ์๋ง ์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ๋ฅผ ํ์ํ๋๋ก ์์ ํ์ต๋๋ค. touchedFields๋ ์ฌ์ฉ์๊ฐ ํ๋๋ฅผ ํ ๋ฒ์ด๋ผ๋ ํด๋ฆญํ๊ฑฐ๋ ํธ์ง์ ์๋ํ๋์ง๋ฅผ ์ถ์ ํ๋ฉฐ, ์ด๋ฅผ ํตํด ์คํ ๋ณ๊ฒฝ ์ ์๋์ผ๋ก ์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง ์๋๋ก ์ ์ดํ ์ ์์์ต๋๋ค.
4. ํ๊ณ
4.1 React Hook Form ์ฌ์ฉ์ ์ฅ๋จ์
์ฅ์ :
- ํผ ์ฑ๋ฅ ์ต์ ํ: ์ด์ ํ๋ก์ ํธ์์ useState๋ก ๋ชจ๋ ํ๋์ ์ํ๋ฅผ ๊ด๋ฆฌํ์ ๋, ๋ณต์กํ ํผ์์ ๋น๋ฒํ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํด ์ฑ๋ฅ ์ ํ๋ฅผ ์ฒด๊ฐํ ์ ์ด ์์ต๋๋ค. ๋ฐ๋ฉด, react-hook-form์ Ref ๊ธฐ๋ฐ์ผ๋ก ์ํ๋ฅผ ๊ด๋ฆฌํด ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ์ต์ํํ๋ฉด์๋ ๋น ๋ฅธ ๋ฐ์์ฑ์ ์ ์งํ ์ ์์์ต๋๋ค.
- API์ ์ง๊ด์ฑ๊ณผ ์ ์ง๋ณด์์ฑ: ์ฒ์ ๋์ ํ ๋๋ ์๋์ ์ผ๋ก ์์ํ์ง๋ง, register, handleSubmit, setError ๊ฐ์ ํจ์๊ฐ ๋งค์ฐ ์ง๊ด์ ์ด๋ผ ๊ธ๋ฐฉ ์ต์ํด์ง ์ ์์์ต๋๋ค. ํนํ, ์ ํจ์ฑ ๊ฒ์ฌ ๋ก์ง์ ํ ๊ณณ์์ ํตํฉ ๊ด๋ฆฌํ๋ฉด์, ์ฝ๋๊ฐ ๊น๋ํด์ง๊ณ ์ ์ง๋ณด์์ฑ์ด ํฌ๊ฒ ํฅ์๋์์ต๋๋ค. ๋์ค์ ์๊ตฌ์ฌํญ์ด ๋ณ๊ฒฝ๋๋๋ผ๋ ํผ์ ๊ตฌ์กฐ์ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์์ฝ๊ฒ ์กฐ์ ํ ์ ์์ด, ๋ฐ๋ณต ์์ ์ด๋ ์์ ํ ๋ถ๋ถ์ด ์ ์์ต๋๋ค.
- ์ ์ฐํ ์ ํจ์ฑ ๊ฒ์ฌ: ๊ธฐ๋ณธ์ ์ธ ์ ํจ์ฑ ๊ฒ์ฌ๋ register ์ต์ ์ ํตํด ์ฝ๊ฒ ์ฒ๋ฆฌํ ์ ์์๊ณ , ๋ณต์กํ ๊ฒ์ฆ ๋ก์ง์ validate ์์ฑ์ ํ์ฉํด ์ ์ฐํ๊ฒ ํ์ฅํ ์ ์์์ต๋๋ค.
๋จ์ :
- ์ด๊ธฐ ์ ์์ ์ด๋ ค์: ์ฒ์์๋ FormProvider์ useFormContext๋ฅผ ์ด๋ป๊ฒ ํ์ฉํด์ผ ํ ์ง, ๊ฐ ํ๋์ ์ํ๋ฅผ ์ด๋ป๊ฒ ์ต์ ํํ ์ง ๊ณ ๋ฏผ์ด ๋ง์ด ๋์์ต๋๋ค. ํนํ ์คํ ๊ธฐ๋ฐ ํผ์์ ๊ฐ ์คํ ์ ๋ฐ๋ผ ํ๋๋ฅผ ๊ด๋ฆฌํ๊ณ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ถ๋ถ์์ ๋ณต์กํจ์ ๋๊ผ์ต๋๋ค. ํ๋ก์ ํธ ์ด๋ฐ์๋ ์๊ฐ๋ณด๋ค ๋ง์ ์๊ฐ์ด ๊ฑธ๋ ธ์ง๋ง, ์ต์ํด์ง๊ณ ๋๋ ์ด ๋ฐฉ์์ด ๋ ์ง๊ด์ ์ด๋ฉด์๋ ๊ฐ๋ ฅํ๋ค๋ ๊ฒ์ ๊นจ๋ฌ์์ต๋๋ค.
4.2 ํฅํ ๊ฐ์ ๋ฐฉํฅ
- ์ ํจ์ฑ ๊ฒ์ฌ ์ต์ ํ: ์๋ฒ์์ ์ ํจ์ฑ ๊ฒ์ฌ ๋ก์ง์ ๋ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๊ธฐ ์ํด ์ปค์คํ ํ ์ ๊ฐ์ ํ ์์ ์ ๋๋ค. ํนํ ์ด๋ฉ์ผ์ด๋ ๊ณ์ ์ด๋ฆ ๊ฐ์ ํ๋์ ๋ํ ์๋ฒ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ๊ฐ์ ํ ๊ณํ์ ๋๋ค.
- ํ ์คํธ ์ฝ๋ ์์ฑ: ๊ฐ ํผ ํ๋์ ๋ํ ์ ํจ์ฑ ๊ฒ์ฌ ๋ฐ ํผ ์ ์ถ ํ ์คํธ๋ฅผ ์งํํ ์์ ์ ๋๋ค. ์ด๋ฅผ ์ํด Jest๋ฅผ ์ฌ์ฉํด ์ ํจ์ฑ ๊ฒ์ฆ ํ ์คํธ๋ฅผ ์์ฑํ์ฌ ํ๋ก์ ํธ์ ์์ ์ฑ์ ๋์ฑ ๊ฐํํ ์์ ์ ๋๋ค.