๐ 06. [์ค๋ฌด] ํผ(Form) ์ฐ๋ ๋ฐ ์คํ๋ผ์ธ-First ์ฑ ์ค๊ณ
๐ ๊ฐ์
React Hook Form๊ณผ React Query๋ฅผ ์ถฉ๋ ์์ด ๊ฒฐํฉํ๋ ๋ ธํ์ฐ์, ๋คํธ์ํฌ๊ฐ ๋๊ธด ์คํ๋ผ์ธ ์ํฉ์์๋ ๋์ํ๋ ์คํ๋ผ์ธ ํผ์คํธ(Offline-First) UX๋ฅผ ์ค๊ณํฉ๋๋ค.
๐ ๋ชฉ์ฐจ
- โ๏ธ ์์ฒ ์ด์ ๊ณ ๋ฏผ: "์ ๋ ฅ์ฐฝ ๋ฐ์ดํฐ๋ ์บ์ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์๊พธ ์ธ์์!"
- ๐ค ์ ์์์ผ ํ๋๊ฐ: ๊ทนํ์ UX ๋ฐฉ์ด์
- 1. ํผ(Form) ์ฐ๋์ ํฉ๊ธ๋ฅ : "์ด๊น๊ฐ์ ๋ถ๋ชจ๊ฐ ๋๊ธฐ๊ณ ์์ ํ๋ผ"
- 2. ์คํ๋ผ์ธ ํผ์คํธ(Offline-First) ์ค๊ณ: "์ธํฐ๋ท์ด ๋๊ฒจ๋ ์ฑ์ ์ด์์ผ ํ๋ค"
- ๐ ๋ฐฐ์ด ๋ด์ฉ ์ ๊ฒํ๊ธฐ (Quiz)
"์์ฒ ๋, ์ ์ ๊ฐ ์ฐ์์์ ํฐ๋ ์ง๋ ๋ ๊ธ ์์ฑ ๋ฒํผ ๋๋ ๋๋ ์๋ฌ ๋๊ณ ๋ฐฉ๊ธ ์ด ๊ธ ํ ๋ฐ๋ฅ ๋ค ๋ ์๊ฐ๋ค๋๋ฐ์?"
โ๏ธ ์์ฒ ์ด์ ๊ณ ๋ฏผ: "์ ๋ ฅ์ฐฝ ๋ฐ์ดํฐ๋ ์บ์ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์๊พธ ์ธ์์!"
(๋ชฉ์์ผ ์คํ, ํผ(Form) ํ๋ฉด์ ๋ฒ๊ทธ ๋ฆฌํฌํธ๋ฅผ ์ฝ๊ณ ๋จธ๋ฆฌ๋ฅผ ์ฅ์ด๋ฏ๋ ์์ฒ )
๐ฃ ์์ฒ : ๋ฆฌ๋ ๋, React Hook Form ๋์
ํด์ ํ์์ ๋ณด ์์ ์ฐฝ ๋ง๋ค์๊ฑฐ๋ ์?
๊ทผ๋ฐ ์ฒซ ํ๋ฉด ๋์ธ ๋ useQuery๋ก ๋ฐ์์จ ์ ์ ์ ๋ณด๋ฅผ ํผ ์ด๊ธฐ๊ฐ์ผ๋ก ๋ฃ์ด์ฃผ๊ณ , ์ ์ ๊ฐ ๋ง ํ์ดํํ๋ค๊ฐ 1๋ถ์ด ์ง๋๋๊น... React Query ๋ฐฑ๊ทธ๋ผ์ด๋ ๋ฆฌํจ์นญ์ด ๋๋ฉด์ useQuery ๋ฐ์ดํฐ๊ฐ ์๋ก ๋ค์ด์ค๊ณ ์ ํผ ์์ฑ ๋ด์ฉ์ด ์น ๋ค ๋ฆฌ์
๋ผ๋ฒ๋ ค์! ์ ์ ๋ค ํญ์๊ฐ ๋น๋ฐ์นฉ๋๋ค ใ
ใ
.
๊ทธ๋ฆฌ๊ณ ํ๋ ๋์! ์์ดํ์ด ์ด์ง ๋๊ฒผ์ ๋ "์ ์ฅ" ๋ฒํผ ๋๋ฅด์ ๋ถ๋ค์ Network Error ๋จ๋ฉด์ ์์ฑ ๋ด์ฉ์ด ํ๊ณต์ผ๋ก ์ฆ๋ฐํด๋ฒ๋ ธ๋์. ๋ฏธ์น๊ฒ ์ด์.
๐ฆ ์ํธ: ์์ฒ ๋, ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ์ง์ ํ ์ง์ฅ 2๊ฐ์ง, ์๋ํฐ(Form) ์ ๊ฒฐ์ ๋คํธ์ํฌ(Offline) ๋ฅผ ๋์์ ๋ง์ผ์
จ๊ตฐ์.
์ฒซ ๋ฒ์งธ ๋ฌธ์ ๋ Server State์ Client State๋ฅผ ํ ๊ณต๊ฐ์ ์์ด๋์ ๋ฒ์ด์ง ์ฐธ์ฌ๊ณ (TkDodo #14),
๋ ๋ฒ์งธ ๋ฌธ์ ๋ React Query v5์ ๋ด์ฅ๋ ์คํ๋ผ์ธ ๋์ ๋ฎคํ
์ด์
(Offline Mutation) ์ต์
์ ์ ์ผ๋ฌ์ ๊ทธ๋ ์ต๋๋ค (TkDodo #13).
์ค์ ์ ๋ฒฝ, ํผ๊ณผ ์คํ๋ผ์ธ์ ์ฐ์ํ๊ฒ ์ ๋ณตํด ๋ด
์๋ค.
๐ค ์ ์์์ผ ํ๋๊ฐ: ๊ทนํ์ UX ๋ฐฉ์ด์
์น ์ฑ์ด ๊ฐ์ฅ ๋ง์ด ๋ฐ์ด๋๋ ๊ณณ์ ์ ์ ๊ฐ ํ
์คํธ๋ฅผ ์
๋ ฅํ๋ ํผ(Form) ํ๋ฉด์
๋๋ค.
์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์์(Read), ์ฌ์ฉ์๊ฐ ๊ทธ๊ฑธ ๊ณ ์น๊ณ (Update), ๋ค์ ์๋ฒ์ ์ด์ผ ํ์ฃ (Mutation).
์ฌ๊ธฐ์ React Query(์๋ฒ์ ์ง์ค)์ React Hook Form(์ ์ ์ ์ ๋ ฅ๊ฐ)์ ํ์ด๋ฐ์ ์๊ฒฉํ ํต์ ํ์ง ๋ชปํ๋ฉด ์์ฒ ์ด์ฒ๋ผ ํผ์ด ๊ฐ์ ๋ฆฌ์ ๋๋ ๋์ฐธ์ฌ๊ฐ ํฐ์ง๋๋ค.
๋ํ ๋ชจ๋ฐ์ผ ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์ ์ธ์ ํฐ๋์ด๋ ์งํ์ฒ ์ ๋ค์ด๊ฐ ์ธํฐ๋ท์ด 10์ด๊ฐ ๋๊ธธ์ง ๋ชจ๋ฆ ๋๋ค. ์ด๋ "์ธํฐ๋ท์ด ๋๊ฒผ์ต๋๋ค" ํ๊ณ ํ๊ฒจ๋ด๋ ์ฑ๊ณผ, "์ผ๋จ ์ ์ฅํด ๋๊ฒ์!" ํ๊ณ ์ธํฐ๋ท์ด ์ฐ๊ฒฐ๋๋ ์๊ฐ ๋ค์์ ๋ชฐ๋ ์ด์ฃผ๋ ์ฑ์ ํด๋์ค ์ฐจ์ด๋ ํ๋๊ณผ ๋ ์ฐจ์ด์ ๋๋ค.
1. ํผ(Form) ์ฐ๋์ ํฉ๊ธ๋ฅ : "์ด๊น๊ฐ์ ๋ถ๋ชจ๊ฐ ๋๊ธฐ๊ณ ์์ ํ๋ผ"
์์ฒ ์ด์ ํผ ์ด๊ธฐํ ์๋ฌ ์์ธ์ Basic 08๊ฐ์์ ์ด์ง ๋ค๋ฃจ์์ต๋๋ค.
useQuery์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ ํผ์ defaultValues์ ์ฐ๋ฌ ๋ฃ์๋๋ฐ, 1๋ถ ๋ค ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์บ์๊ฐ ๊ฐฑ์ ๋์ ๊ทธ ๋ฐ์ดํฐ๊ฐ ๋ค์ ํผ์ ๋๋ ค๋ฒ๋ฆฐ ๊ฑฐ์ฃ .
์ด๋ฅผ ๋ง์ผ๋ ค๋ฉด ๋ทฐ(View)๋ฅผ ๋ถ๋ฆฌํด์ผ ํฉ๋๋ค!
๋ฐ์ดํฐ๋ฅผ ํผ์ค๊ธฐ๋ง ํ๋ ๋ถ๋ชจ(Data Fetcher) ์ ํผ๋ง ๊ฐ์ง๊ณ ๋
ธ๋ ์์(Form Editor) ์ผ๋ก ๋ง์ด์ฃ .
// ๐ ๋ถ๋ชจ ์ปดํฌ๋ํธ: ์ค์ง ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ๋๊ฒจ์ฃผ๊ธฐ๋ง ํ๋ค.
function UserProfilePage() {
const { data: user } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
if (!user) return <Spinner />;
// ๐ ํฉ๊ธ๋ฅ 1: ๋ฐ์ดํฐ๊ฐ ์์ ํ ์ค๋น๋ ์ํ์์๋ง ์์ ํผ์ ๊ทธ๋ฆฐ๋ค.
return <UserProfileForm initialData={user} />
}// ๐ ์์ ์ปดํฌ๋ํธ: React Hook Form (์ค์ง ํด๋ผ์ด์ธํธ ์ํ)
import { useForm } from 'react-hook-form';
type FormValues = { name: string; age: number };
function UserProfileForm({ initialData }: { initialData: User }) {
const { register, handleSubmit } = useForm<FormValues>({
// ๐ ํฉ๊ธ๋ฅ 2: ๋ถ๋ชจ๊ฐ ์ค ๋ฐ์ดํฐ๋ฅผ "์ต์ด์ ์ด๊น๊ฐ"์ผ๋ก๋ง ์ฐ๊ณ ๋ค๋ ๋์๋ณด์ง ์๋๋ค.
defaultValues: { name: initialData.name, age: initialData.age },
});
const mutation = useMutation({
mutationFn: updateUser,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['user'] }),
});
const onSubmit = (values: FormValues) => {
mutation.mutate(values);
};
return <form onSubmit={handleSubmit(onSubmit)}>...</form>
}์ด๋ ๊ฒ ํธ๋ฆฌ๋ฅผ ์ชผ๊ฐ๋ฉด ๋ถ๋ชจ ์ชฝ React Query๊ฐ 1๋ถ๋ง๋ค ๋ฐฑ๊ทธ๋ผ์ด๋ ํต์ ์ ํ๋ ๋ง๋ , ์์ ์ชฝ์ useForm์ ์ฒ์ ๋๊ฒจ๋ฐ์ ์ด๊น๊ฐ๋ง ์ฅ๊ณ ์๊ธฐ ๊ฐ ๊ธธ์ ๊ฐ๊ธฐ ๋๋ฌธ์ ์ ์ ๊ฐ ํ์ดํํ๋ ๊ธ์๊ฐ ๋ฆฌ์
๋๋ ์ผ์ด 0%๊ฐ ๋ฉ๋๋ค.
2. ์คํ๋ผ์ธ ํผ์คํธ(Offline-First) ์ค๊ณ: "์ธํฐ๋ท์ด ๋๊ฒจ๋ ์ฑ์ ์ด์์ผ ํ๋ค"
์, ๋ ๋ฒ์งธ ๋ฌธ์ ! ์ ์ ๊ฐ ํผ์ ๋ค ์์ฑํ๊ณ "์ ์ฅ" ๋ฒํผ์ ๋๋ ๋๋ฐ ํํ ์งํ์ฒ ์์ดํ์ด๊ฐ ๋๊ฒผ์ต๋๋ค. ๋ฐ๋๋ผ fetch ์๋ค๋ฉด ์ฆ์ Uncaught Network Error๊ฐ ๋ ์ ๋นจ๊ฐ ์๋ฆผ์ฐฝ์ ๋ฟ์๊ฒ ์ฃ .
ํ์ง๋ง React Query์๋ ๋ฌด์๋ฌด์ํ ๊ธฐ๋ณธ๊ฐ์ด ์จ์ด์์ต๋๋ค.
๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ useQuery ๋ ํต์ ์ด ์คํจํ๋ฉด ์๋์ผ๋ก 3๋ฒ๊น์ง ์ฌ์๋(Retry) ํ๊ณ , ์คํ๋ผ์ธ ์ํ์ผ ๋๋ ์ธํฐ๋ท์ด ์ฐ๊ฒฐ๋ ๋๊น์ง ์ผ์ ์ ์ง(Pause) ์ํ๋ก ์จ์ฃฝ์ด๊ณ ๊ธฐ๋ค๋ฆฝ๋๋ค (TkDodo ํผ์
, ์ต๊ณ ์ ๋ฏธ๋).
๊ทธ๋ฐ๋ฐ ์ฌ๊ธฐ์ ํจ์ ์นด๋!
๋ฐ์ดํฐ๋ฅผ ์๋ฒ๋ก ๋ณด๋ด๋(์์ ํ๋) useMutation ์ ๊ธฐ๋ณธ์ ์ผ๋ก Retry๊ฐ 0๋ฒ(๊บผ์ ธ์์)์
๋๋ค. ์๋ํ๋ฉด ๊ฒฐ์ ๋ฒํผ์ 3๋ฒ ์ฐ๋ฌ์ ๋๋ฅด๋ ์ฌ๊ณ ๊ฐ ํฐ์ง๊น ๋ด ๋ณด์์ ์ผ๋ก ์ธํ
๋ ๊ฑฐ๋๊น์.
์คํ๋ผ์ธ ํ(Offline Mutation Queue) ํด์ ๋ฒ
๋๊ธ ์์ฑ, ์ข์์, ์ผ๋ฐ์ ์ธ ๊ธ ์์ ๊ฐ์ ์์ ํ(Idempotentํ ๋๋์) Mutation์ ํํ์ฌ, ์ฐ๋ฆฌ๋ ์ด๋ค์ ์ผ์ ์ ์ง(Pause)์์ผ๋๋ค๊ฐ ์ธํฐ๋ท์ด ๋ณต๊ตฌ๋๋ ์๊ฐ ์ผ์ ํ ํฌํ ํ ์ ์์ต๋๋ค!
// ๐ ์ปค์คํ
Mutation ํ
export const useSavePost = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: savePostApi,
// ๐ ๋น๊ธฐ 1: "์คํ๋ผ์ธ์ผ ๋ ์๋ฌ๋ก ํ๊ฒจ๋ด์ง ๋ง๊ณ , ์ผ์ ์ ์ง(Pause)์์ผ์ ํ์ ๋ด์๋ผ!"
networkMode: 'offlineFirst',
// ๐ ๋น๊ธฐ 2: "๋คํธ์ํฌ ๋๊ฒจ์ ์คํจํ์ด? ๊ทธ๋ผ ์ธํฐ๋ท ๋ณต๊ตฌ๋ ๋๊น์ง 3๋ฒ์ ๋ค์ ์๋ํด๋ด!"
retry: 3,
onMutate: async (newPostInfo) => {
// ๐ ๋๊ด์ ์
๋ฐ์ดํธ ๋ฐ๋!
// ์ธํฐ๋ท์ด ๋๊ฒจ์ ์๋ฒ๊ฐ "์๋ต"์ ์ ์คฌ์์๋ ๋ถ๊ตฌํ๊ณ
// ํ๋ฉด์๋ ๋ฌด์กฐ๊ฑด ์๋ก ์ด ๊ธ์ ๋น์ฅ ๊ทธ๋ ค์ ์ ์ ๋ง์์ ํธ์ํ๊ฒ ๋ง๋ญ๋๋ค.
await queryClient.cancelQueries({ queryKey: ['posts'] });
/* ... ์์ 06๊ฐ์ฒ๋ผ setQueryData ๋ก ์บ์ ์กฐ์ ... */
},
});
};์ด๋ ๊ฒ ์ธํ ํ๊ณ ์ ์ ๊ฐ ๋นํ๊ธฐ ๋ชจ๋์์ ํผ์ ์ ์ก ์ณ๋ณด์ธ์.
- ๋๊ด์ ์ ๋ฐ์ดํธ ๋๋ถ์ ํ๋ฉด์๋ ๋ด ๊ธ์ด ๋ฉ์ง๊ฒ ๋ฑ๋ก๋ ๊ฒ์ฒ๋ผ ๋ณด์ ๋๋ค.
- ๋ท๋จ์์ Mutation์ ์คํจํ์ง๋ง ํฐ์ง์ง ์๊ณ ๋๊ธฐ์ด(Queue) ์ ๋ค์ด๊ฐ ์๋ฉด์ ์ทจํฉ๋๋ค. (
isPaused === true) - 30๋ถ ๋ค ์ ์ ๊ฐ ์์ดํ์ด ์กด์ ๋ค์ด์ ํธ๋ํฐ ๋ฐ์ดํฐ๊ฐ ๋ค์ ์ฐ๊ฒฐ(
online์ด๋ฒคํธ ๋ฐ๋)๋ฉ๋๋ค! - React Query ํ์ ์ ๋ค์ด์๋ Mutation์ด ์ฆ๊ฐ์ ์ผ๋ก ์๋ฒ๋ก ๋ฐ์ฌ(Retry)๋๋ฉฐ DB์ ์ ๋ง๋ก ์ ์ฅ๋ฉ๋๋ค!
์ด ํ๋ฆ์ด์ผ๋ง๋ก ๋ทํ๋ฆญ์ค, ์ธ์คํ๊ทธ๋จ์์๋ ๋ณผ ๋ฒํ ์ด์ผ๋ฅ ์คํ๋ผ์ธ ํผ์คํธ(Offline-First) UX ์ํคํ ์ฒ์ ๋๋ค.
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์, ์คํ๋ผ์ธ ๋ชจ๋์์ ์๋ฌ ์ ๋๊ณ ๋ฐฑ๊ทธ๋ผ์ด๋ ํ(Queue)์ ์์ ๋จ๋ค๊ฐ ๋์ค์ ์ด์ฃผ๋ ๊ฑฐ ์ง์ง ์ปฌ์ฒ์ผํฌ๋ค. ์ด๊ฑฐ ๋ด๊ฐ ์๋์ผ๋ก ์งฐ์ผ๋ฉด ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ๋ฐ์ดํฐ ์บ์ฑํ๊ณ , window.addEventListener('online', ...) ๋ฌ๊ณ ๋ฐฑ๋ ์์๋ฅผ ํ์ ํ
๋ฐ, ์ต์
๋ ๊ฐ(networkMode, retry)๋ก ๋๋๋ค๊ณ ...?
๐ก ์ค๋์ ๊ตํ: "Form ๋ฐ์ดํฐ์ Query ์บ์๋ ์ฒ ์ ํ ๋ถ๋ชจ/์์ ๊ด๊ณ๋ก ์ชผ๊ฐ์ ์ด๊ธฐํ ๊ฐ์ญ์ ๋์ด๋ผ! ๊ทธ๋ฆฌ๊ณ ๊ฒฐ์ ์ฒ๋ผ ๋ฏผ๊ฐํ ๊ฒ ์๋ ๋ชจ๋ Mutation(์ข์์, ๋ฆฌ์คํธ ์์ ๋ฑ)์ ๊ณผ๊ฐํ๊ฒ ์คํ๋ผ์ธ Retry ํ๋ฅผ ์ด์ด๋ฌ์ ๊ฒฐ์ ๋คํธ์ํฌ UX๋ฅผ ์๋ฒฝํ ๋ง์๋ผ!"
์ด๊ฑฐ ์ฑ์ ๋ฐ์ํ๊ณ ์งํ์ฒ ์ถํด๊ทผ๋ฌ(์์ ๋์์ด๋ ๋)ํํ ์์ฐ ๋ณด์ฌ๋๋ ธ๋๋ ๊ธฐ๋ฆฝ๋ฐ์ ๋ฐ์๋ค. "์์ฒ ๋, ํฐ๋ ์ง๋๊ฐ ๋๋ง๋ค ์ฐ๋ฆฌ ์ฑ ๋งจ๋ ํฐ์ ธ์ ์ง์ฆ ๋ฌ๋๋ฐ ์ด์ ๋๊ธ ๋ง๋๋ก ์จ์ ธ์ ใ ใ " ์บฌ.. ๊ฐ๋ฐ์ ํ ๋ง ๋๋ค ใ ใ ใ ๋ด์ผ์ ๋๋์ด ํ ์คํธ ํ๊ฒฝ ๋ง์ง๋ ๋ง์ง๋ง ๊ฐ์ข๋ค. ํ์ดํ !
๐ ๋ฐฐ์ด ๋ด์ฉ ์ ๊ฒํ๊ธฐ (Quiz)
Q. ์์ฒ ์ด๋ ๋ค์๊ณผ ๊ฐ์ด useQuery์์ ๋ฐ์์จ ๋ฐ์ดํฐ์ ์ปดํฌ๋ํธ ๋ด๋ถ์ ๋ก์ปฌ useState (ํํฐ์ฉ ์
๋ ฅ ์ํ)๋ฅผ ํ๋์ ํ์ผ ์์ ๋ชจ๋ ๋ฃ์ด๋์์ต๋๋ค. ๋ค์ ์ค, ์ด ์ปดํฌ๋ํธ๊ฐ ์ฑ๋ฅ์ ๋ฌธ์ ๋ฅผ ์ผ์ผํค๊ณ (๋๋ ๋ฒ๊ทธ์ ์จ์์ด ๋๊ณ ) ์๋ ์ด์ ๋ฅผ Form/์ปดํฌ๋ํธ ๋ถ๋ฆฌ ์์น์ ์
๊ฐํ์ฌ ๊ฐ์ฅ ์ ํํ๊ฒ ์ง์ ๊ฒ์ ๋ฌด์์
๋๊น?
function BadSearchPage() {
const { data } = useQuery({ queryKey: ['orders'], queryFn: fetchOrders });
const [filterText, setFilterText] = useState("");
const filteredData = data?.filter(item => item.name.includes(filterText));
return (
<div>
<input value={filterText} onChange={e => setFilterText(e.target.value)} />
<ul>{filteredData?.map(...)}</ul>
</div>
)
}- A)
useState๊ฐ ์ ๋ ฅ๋ ๋๋ง๋ค (ํค๋ณด๋ ์น ๋๋ง๋ค)useQuery๋ํ ๋งค๋ฒ ๋ฆฌ๋ ๋๋ง๋์ด ์๋ฒ์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ณต Fetch๋ฅผ ์๊ฒ ๋๊ธฐ ๋๋ฌธ์ด๋ค. - B)
filterTextํ์ดํ์ผ๋ก ์ธํด ์ปดํฌ๋ํธ๊ฐ ๋ค์ ๋ ๋๋ง๋ ๋๋ง๋ค, ๊ตณ์ด ์ ๋ฐ๋์ด๋ ๋๋ ๊ป๋ฐ๊ธฐ ๋ก์ง๋ค์ด ์ฌ์คํ๋๊ณ , ๋ฐ๋๋ก ์๋ฒdata๊ฐ ๋์ฐฉํ๊ฑฐ๋ ๋ฐฑ๊ทธ๋ผ์ด๋ ๋ฆฌํจ์นญ ๋ ๋๋ง๋ค ํ์ดํ ์ค์ด๋ UI๊ฐ ๋ ๋ ๋ฒ๋ฒ ์์ ์ผ์ผํค๋ "์ํ ๋ฒ์ ์นจ๋ฒ" ์ด ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ด๋ค. - C)
useState๋์ ๋ฐ๋์ ์ ์ญ ์ํ(Zustand)๋ฅผ ์จ์ผ๋ง ํํฐ๊ฐ ์ ์ฉ๋๊ธฐ ๋๋ฌธ์ด๋ค.
โ
์ ๋ต: B
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
: React Query๋ ๊ธฐ๋ณธ์ ์ผ๋ก A๋ฒ์ฒ๋ผ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋๋ค๊ณ ํด์ ๋คํธ์ํฌ ์์ฒญ์ ๋ฌด์ง์ฑ์ผ๋ก ๋ค์ ์์ง ์์ต๋๋ค (์บ์๊ฐ ์ ์ดํจ). ์ง์ง ์ฌ๊ฐํ ๋ฌธ์ ๋ B๋ฒ์
๋๋ค! ์ฌ์ฉ์๊ฐ ํค๋ณด๋๋ฅผ 'A', 'p', 'p', 'l', 'e' ์น ๋๋ง๋ค
setFilterText๊ฐ ํฐ์ง๋ฉฐ ์ ๊ฑฐ๋ํ ์ปดํฌ๋ํธ๋ฅผ 5๋ฒ ์ฌ๋ ๋ํฉ๋๋ค. ๋ง์ฝ ํ๋ฉด ํ๋จ์ ๋ฌด๊ฑฐ์ด ๋ก์ง์ด ์๋ค๋ฉด ํค๋ณด๋ ํ์ดํ๋ง๋ค ์ฑ ์ ์ฒด๊ฐ ๋๊น๋๋ค. - ์ค๋ต ํผ๋๋ฐฑ: "์์ฒ ๋, ํด๋ผ์ด์ธํธ ์
๋ ฅ ์ํ(
input)์ ์๋ฒ ๋ ๋ ์ํ(data list)๋ ์๋ช ์ฃผ๊ธฐ(Lifecycle)๊ฐ ์์ ํ ๋ค๋ฆ ๋๋ค!Input์ฐฝ์ ๋ณ๋์ ์ค๋งํธ ์ปดํฌ๋ํธ๋ก ๋นผ์(์์์ผ๋ก ๋ถ๋ฆฌ) ์ ๋ ฅ๊ฐ๋งdebounceํ์์ ์๋ก ์ด์ฃผ๋๊ฐ, ์๋๋ฉด ๋ฆฌ์คํธ ์ปดํฌ๋ํธ ์์ฒด๋ฅผ ์์์ผ๋ก ๋ถ๋ฆฌํด์ ๋ ๋๋ง ์ถฉ๋์ ์ชผ๊ฐ์ผ ํ๋ ์ ์ ํ๋ฅผ ๋ง์ ์ ์์ด์." - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: ์ ๋ ฅ์ฐฝ(Form/Input)๊ณผ ์กฐํ์ฐฝ(Query List)์ ์ปดํฌ๋ํธ๋ฅผ ์ฐข์ด๋ผ! ๋ ๋๋ง ๊ฐ์ญ ๊ธ์ง!