당신이 쓰지 않는 최고의 폼 라이브러리, mobx-react-form가 왜 최고의 선택인지.
출처: Dev.to
React에서 폼을 만든다면 Formik, React Hook Form, final‑form 등을 사용해봤을 겁니다. 이들 모두 훌륭합니다. 하지만 복잡하고, 깊게 중첩된, 다단계 폼—인생 선택을 의심하게 만드는 그런 폼—을 다루어야 할 때는 더 나은 도구가 있습니다.
mobx-react-form은 MobX 반응성을 강력한 폼 상태 엔진과 결합합니다. 왜 주목해야 하는지 살펴보세요.
1. 중첩 필드도 손쉽게
실제 폼은 평평하지 않습니다. 예를 들어 청구서에는 고객 정보, 배송 주소, 그리고 각각 이름·수량·가격을 가진 가변적인 상품 목록이 있습니다.
MRF는 이를 그대로 지원합니다:
const fields = [
'customer.name',
'customer.email',
'shipping.address',
'shipping.city',
'shipping.zip',
'products',
'products[].name',
'products[].quantity',
'products[].unitPrice',
];
각 경로는 자체 검증, 오류, 더티(dirty) 추적을 갖는 관찰 가능한 필드가 됩니다. 평탄화도, 정규화도 필요 없습니다. 값은 API가 기대하는 정확한 구조로 직렬화됩니다:
{
"customer": { "name": "...", "email": "..." },
"shipping": { "address": "...", "city": "...", "zip": "..." },
"products": [
{ "name": "Widget A", "quantity": 2, "unitPrice": 19.99 }
]
}
그리고 v6.15부터는 오류가 자동으로 버블링됩니다:
const form = new MobxReactForm({ fields }, {
options: { bubbleUpErrorMessages: true }
});
// products[0].product 가 비어 있을 때, 아래와 같이 바로 사용할 수 있습니다:
{form.error && <span>{form.error}</span>}
2. 검증 플러그인 — 원하는 것을 직접 사용
MRF는 하나의 검증기에 얽매이지 않습니다. 기본 제공 6가지 검증 플러그인을 지원합니다:
| 플러그인 | 패키지 | 스타일 |
|---|---|---|
| DVR | validatorjs | 선언형 규칙 ('required') |
| VJF | Custom functions | {(value) => [isValid, message]} |
| ZOD | zod | 스키마 기반 |
| YUP | yup | 스키마 기반 |
| JOI | joi | 스키마 기반 |
| SVK | ajv | 스키마 기반 |
간단한 필드에는 DVR을, 복잡한 교차 필드 검증에는 ZOD를 같은 폼 안에서 동시에 사용할 수 있습니다:
plugins() {
return {
dvr: dvr({ package: validatorjs }),
zod: zodPlugin({ package: z, schema: invoiceSchema }),
};
}
전체 폼에 대한 Zod 스키마를 정의하면 클라이언트‑사이드 검증과 TypeScript 타입을 한 곳에서 얻을 수 있습니다:
const invoiceSchema = z.object({
customer: z.object({
name: z.string().min(2),
email: z.string().email(),
}),
products: z.array(z.object({
name: z.string().min(1),
quantity: z.number().min(1),
unitPrice: z.number().positive(),
})).min(1),
});
3. 완전한 TypeScript 제네릭 (v6.13)
TypeScript를 사용한다면, MRF는 이제 끝‑끝 타입 안전성을 제공합니다:
interface InvoiceForm {
customer: { name: string; email: string };
shipping: { address: string; city: string; zip: string };
products: Array<{ name: string; quantity: number; unitPrice: number }>;
}
const form = new MobxReactForm({ fields });
form.$('products[0]').$('name'); // Field — 완전 타입 지정
form.values(); // InvoiceForm — 완전 타입 지정
form.$('customer').$('name').set('Acme Corp'); // 타입‑안전 setter
자동 완성을 위한 PathsOf 도 지원합니다:
form.$('ship') // → 에디터가 제안: 'shipping.address' | 'shipping.city' | 'shipping.zip'
필드 경로를 추측할 필요가 없고, 오타로 인한 런타임 오류도 사라집니다.
4. 동적·배열 필드
상품을 동적으로 추가하거나, 배송 주소를 리스트에서 제거해야 할 때? 바로 사용할 수 있습니다.
// 상품 추가
form.$('products').add({
product: 'New Product',
quantity: 1,
unitPrice: 0,
});
// 두 번째 상품 삭제
form.$('products').del(1);
// 순서 변경 (v6.14부터 ArrayMap 지원)
form.$('products').move(0, 2);
배열, 중첩 배열, 동적 필드 생성—모두 MobX 반응성으로 처리됩니다. 각 아이템은 자체 검증 수명 주기, 더티 추적, 오류 상태를 가집니다.
5. Composer — 다중 폼 오케스트레이션
독립적인 폼들을 한 번에 제출해야 할 때? composer() 함수가 폼 간 검증을 조율합니다.
const checkout = composer({ billing, shipping, payment });
checkout.validate().then(({ valid, errors, values }) => {
if (valid) {
submitOrder({
...values.billing,
shipping: values.shipping,
payment: values.payment,
});
} else {
// errors 는 폼 이름을 키로 갖는 객체: { billing: {...}, shipping: {...}, payment: {...} }
showValidationSummary(errors);
}
});
결제 흐름, 다중 탭 대시보드, 단계별 마법사 UI 등 각 단계가 독립적인 폼인 경우에 완벽합니다.
6. UI 라이브러리와 무관
MRF는 모든 주요 UI 라이브러리와 함께 사용할 수 있으며, 특정 라이브러리를 강제하지 않습니다:
- Material UI
- Ant Design
- React Aria
- Headless UI
- React Widgets
- React‑Select
- 순수 HTML
각 라이브러리마다 얇은 바인딩 레이어만 제공됩니다. 폼 로직은 MUI TextField든, 순수 <input>이든 동일하게 동작합니다.
// UI 라이브러리와 무관하게 동일하게 동작
<input type="text" {...field.bind()} />
MaterialTextField → AntdInput → SimpleInput 으로 바꾸어도 폼 상태는 전혀 신경 쓰지 않습니다.
7. 프로덕션 검증, 오랜 유지보수
MRF는 8년 이상 동안 프로덕션 폼을 구동해 왔습니다. 1.1k+ GitHub 스타를 보유하고 있으며, 최신 릴리즈(2026년 6월)에서는 가장 많이 요청된 기능들을 도입했습니다:
- v6.15 — 중첩 폼에서 오류 버블링
- v6.14 — 예측 가능한 정렬과 드래그‑앤‑드롭을 지원하는 ArrayMap
- v6.13 — 완전한 TypeScript 제네릭, strict null 체크, 경로 자동 완성
- v6.12 — 커스텀 검증 타입 추론, YUP
.ref()지원 - v6.11 — 완전한 null 값 지원
각 릴리즈는 가상의 사용 사례가 아니라 실제 프로덕션 요구에 의해 추진되었습니다.
결론
| 시나리오 | MRF | 대안 |
|---|---|---|
| 간단한 로그인 폼 | ✅ 과잉일 수 있지만 동작함 | ✅ 좋은 선택 |
| 깊게 중첩된 필드 (청구서, 주문, 프로필) | ✅ 네이티브 지원 | ❌ 평탄화 필요 |
| 동적 배열 (추가/삭제/재정렬) | ✅ 내장 기능 | ❌ 수동 상태 관리 |
| 다중 폼 검증 (체크아웃, 마법사) | ✅ Composer | ❌ 직접 오케스트레이션 |
| 검증 플러그인 (DVR/ZOD/YUP/JOI 등) | ✅ 7개 플러그인 | ❌ 보통 1~2개만 |
| TypeScript 제네릭 + 경로 자동 완성 | ✅ v6.13+ | ❌ 제한적 |
| 중첩 폼 오류 버블링 | ✅ v6.15+ | ❌ 수동 재귀 |
| UI 라이브러리 유연성 | ✅ 모든 라이브러리 | ✅ 모든 라이브러리 |
로그인 페이지를 넘어 청구서, 체크아웃, 다단계 회원가입, 동적 섹션을 가진 관리자 패널 등을 만든다면 MRF를 한 번 써보세요. 복잡함을 MRF가 처리해 주니 여러분은 비즈니스 로직에 집중하면 됩니다.
npm install --save mobx-react-form
실시간 데모 체험하기 | GitHub | **[
