phased-development
Fast and concise JavaScript, TypeScript utilities for React.
- Category: discovered
- Tags: github, community
- Version: 1.0.0
- Author: walid
- Downloads: 0
- GitHub stars: 1
- Updated: 2025-11-21
Download the latest version (v1.0.0) from this page.
SKILL.md
# Phased Development Skill
## 역할
당신은 @canard/schema-form 플러그인의 단계별 개발 프로세스 전문가입니다.
## 핵심 책임
1. **5단계 개발 절차**: 설계 → 인프라 → 핵심 → 고급 → 최적화
2. **우선순위 결정**: 컴포넌트 구현 순서 제시
3. **체크리스트 제공**: 각 단계별 완료 조건
4. **마일스톤 관리**: 개발 진행 상황 추적
5. **품질 검증**: 각 단계별 검증 기준
## 작동 방식
### 1. 개발 단계 가이드
**knowledge/development-phases.md**를 통해:
- Phase 1-5 상세 절차
- 각 단계 완료 조건
- 다음 단계 전환 기준
### 2. 우선순위 관리
**knowledge/priority-guide.md**로:
- P1-P4 우선순위 기준
- 컴포넌트 구현 순서
- MVP vs Full Feature 구분
### 3. 최적화 검증
**knowledge/optimization-checklist.md**로:
- 성능 최적화 체크리스트
- 코드 품질 검증
- 배포 전 최종 점검
## 제공하는 정보
### 5단계 개발 프로세스
#### Phase 1: 설계 및 검증 (1-2일)
**목표**: 호환성 분석 및 설계 완료
**작업**:
1. UI 라이브러리 컴포넌트 매핑 분석
2. Context 타입 및 인터페이스 설계
3. 구현 우선순위 결정
4. package.json 초안 작성
**완료 조건**:
- [ ] 호환성 매트릭스 작성 완료
- [ ] Context 타입 정의 완료
- [ ] 우선순위 결정 완료
#### Phase 2: 기본 인프라 (2-3일)
**목표**: 프로젝트 기본 구조 구축
**작업**:
1. 프로젝트 설정 및 의존성 설치
2. 타입 정의 (`src/type.ts`)
3. 기본 렌더러 구현 (FormGroup, FormLabel, FormInput, FormError, formatError)
4. 빌드 및 테스트 환경 구성
**완료 조건**:
- [ ] 빌드 성공 (`yarn build`)
- [ ] 타입 체크 통과 (`yarn type-check`)
- [ ] 기본 렌더러 5개 구현 완료
#### Phase 3: 핵심 컴포넌트 (3-5일)
**목표**: 필수 FormTypeInput 구현
**우선순위 순서**:
1. **Priority 1: 기본 Input** (1-2일)
- FormTypeInputString
- FormTypeInputNumber
- FormTypeInputBoolean
- **formTypeInputDefinitions 배열 마지막에 배치**
2. **Priority 2: 특수 Format/FormType** (1-2일)
- FormTypeInputTextarea (format: textarea)
- FormTypeInputPassword (format: password)
- FormTypeInputDate (format: date)
- FormTypeInputTime (format: time)
- FormTypeInputSlider (formType: slider)
- **formTypeInputDefinitions 배열 앞쪽에 배치**
3. **Priority 3: Enum 및 구조** (1-2일)
- FormTypeInputStringEnum (함수 조건)
- FormTypeInputArray (ChildNodeComponents)
**완료 조건**:
- [ ] P1 컴포넌트 모두 구현
- [ ] P2 컴포넌트 선택 구현
- [ ] formTypeInputDefinitions 우선순위 순서 정렬
- [ ] 단위 테스트 작성
#### Phase 4: 고급 기능 및 문서화 (2-3일)
**목표**: 추가 컴포넌트 및 문서
**작업**:
1. 선택적 컴포넌트 (Radio, Slider, 등)
2. README.md 및 README-ko_kr.md 작성
3. Storybook stories 작성
4. package.json 의존성 최종 확인
**완료 조건**:
- [ ] README 작성 완료
- [ ] Storybook stories 작성
- [ ] 예제 코드 작성
#### Phase 5: 최적화 및 검증 (1-2일)
**목표**: 성능 최적화 및 배포 준비
**작업**:
1. 성능 최적화 체크리스트 적용
2. 접근성 검증 (axe-core)
3. canard-form 통합 테스트
4. 빌드 및 타입 체크
**완료 조건**:
- [ ] 성능 최적화 체크리스트 100% 완료
- [ ] 접근성 테스트 통과
- [ ] 통합 테스트 통과
- [ ] 빌드 크기 확인
## 우선순위 가이드
### Priority 1 (P1) - MVP 필수
**기준**: 기본 폼 동작에 필수
**컴포넌트**:
- String, Number, Boolean 입력
- 기본 렌더러 5개
**목표**: 2-3일 내 구현
### Priority 2 (P2) - 초기 버전 포함
**기준**: 일반적으로 자주 사용
**컴포넌트**:
- Textarea, Password, Email
- Date, Time (UI 라이브러리 지원 시)
- Enum/Select
**목표**: Phase 3-4에서 구현
### Priority 3 (P3) - 추가 기능
**기준**: 특수한 경우 사용
**컴포넌트**:
- Radio Group, Slider
- Color Picker
- 커스텀 Format
**목표**: Phase 4 또는 2차 버전
### Priority 4 (P4) - 선택적
**기준**: 외부 라이브러리 필요 또는 비표준
**컴포넌트**:
- Rich Text Editor
- File Upload (고급)
**목표**: 3차 버전 또는 별도 패키지
## 체크리스트 템플릿
### Phase 완료 체크리스트
```markdown
## Phase {N}: {Phase Name}
**시작일**: YYYY-MM-DD
**목표 완료일**: YYYY-MM-DD
**실제 완료일**:
### 작업 목록
- [ ] 작업 1
- [ ] 작업 2
- [ ] 작업 3
### 완료 조건
- [ ] 조건 1
- [ ] 조건 2
### 이슈 및 결정사항
- [날짜] 이슈 또는 결정
### 다음 단계
- Phase {N+1} 준비사항
```
## 마일스톤
### M1: MVP (Phase 1-3)
- 기본 렌더러 5개
- P1 컴포넌트 (String, Number, Boolean)
- 빌드 및 테스트 환경
**목표**: 1주일
### M2: 초기 버전 (Phase 4)
- P2 컴포넌트 추가
- 문서화 완료
- Storybook stories
**목표**: 2주일
### M3: 정식 버전 (Phase 5)
- 성능 최적화
- 접근성 검증
- 통합 테스트
**목표**: 3주일
## 제약 조건
- Phase 순서는 지키되, 일정은 유연하게
- P1 완료 전 P2 시작 금지
- 각 Phase 완료 조건 충족 필수
- 코드 리뷰는 Phase 단위로
## 출력 형식
### 개발 계획 제공
```markdown
## {UI Library} 플러그인 개발 계획
### 일정 요약
- Phase 1: {날짜}
- Phase 2: {날짜}
- Phase 3: {날짜}
- Phase 4: {날짜}
- Phase 5: {날짜}
### Phase 1: 설계 및 검증
[상세 내용]
### Phase 2: 기본 인프라
[상세 내용]
...
```
## 사용 시나리오
### 시나리오 1: MUI 플러그인 Phase 1 - 설계 및 검증
**상황**: 새로운 MUI (Material-UI) v6 플러그인 개발 시작, Phase 1 설계 단계
**단계**:
#### Step 1: 호환성 매트릭스 작성
```markdown
## MUI v6 호환성 분석
### 기본 컴포넌트 매핑
| FormTypeInput | MUI Component | 우선순위 | 난이도 | 비고 |
|---------------|---------------|----------|--------|------|
| String | TextField | P1 | 쉬움 | 기본 |
| Number | TextField (type="number") | P1 | 쉬움 | 기본 |
| Boolean | Checkbox / Switch | P1 | 쉬움 | 기본 |
| Textarea | TextField (multiline) | P2 | 쉬움 | format: textarea |
| Password | TextField (type="password") | P2 | 쉬움 | format: password |
| Email | TextField (type="email") | P2 | 쉬움 | format: email |
| Date | DatePicker (@mui/x) | P2 | 중간 | 별도 패키지, ISO 변환 |
| Time | TimePicker (@mui/x) | P2 | 중간 | 별도 패키지, ISO 변환 |
| StringEnum | Select / RadioGroup | P2 | 중간 | 개수 기반 선택 |
| NumberEnum | Select | P2 | 중간 | - |
| Slider | Slider | P3 | 중간 | formType: slider |
| Array | Box + Button | P2 | 어려움 | ChildNodeComponents |
| Object | Box | P2 | 쉬움 | ChildNodeComponents |
### 특수 고려사항
**DatePicker/TimePicker**:
- ✅ 장점: @mui/x-date-pickers 공식 지원
- ⚠️ 주의: 별도 패키지 필요, date-fns 어댑터 설치
- 🔧 구현: ISO 8601 ↔ Date 객체 변환 필수
- 📦 의존성: `@mui/x-date-pickers`, `date-fns`
**Slider**:
- ✅ 장점: MUI Slider 컴포넌트 기본 제공
- 🔧 구현: formType: 'slider' 조건 추가
- 📋 Schema 예시: `{ type: 'number', formType: 'slider', minimum: 0, maximum: 100 }`
**Array/Object**:
- ✅ 장점: Box, Paper 컴포넌트로 깔끔한 레이아웃 가능
- 🔧 구현: ChildNodeComponents 활용, IconButton으로 삭제
- 🎨 스타일: Paper + sx prop으로 depth 표현
```
#### Step 2: Context 타입 설계
```typescript
// src/type.ts - 초안
/**
* MUI 플러그인 Context 타입
* SchemaForm의 context prop으로 전달되는 설정
*/
export interface MuiFormContext {
/**
* 전역 컴포넌트 크기
* @default 'medium'
*/
size?: 'small' | 'medium' | 'large';
/**
* 전역 TextField variant
* @default 'outlined'
*/
variant?: 'standard' | 'outlined' | 'filled';
/**
* 전역 색상 테마
* @default 'primary'
*/
color?: 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning';
/**
* 파일 업로드 콜백 (Phase 4 또는 2차 버전)
* @param file - 업로드할 파일
* @returns 업로드된 파일 URL
*/
onFileAttach?: (file: File) => Promise<string>;
/**
* 날짜 포맷 로케일 (Phase 3)
* @default 'ko' (한국어)
*/
dateLocale?: string;
}
/**
* SchemaForm 사용 예시:
*
* <SchemaForm
* schema={schema}
* plugin={muiPlugin}
* context={{
* size: 'medium',
* variant: 'outlined',
* color: 'primary',
* dateLocale: 'en-US',
* }}
* />
*/
```
#### Step 3: 우선순위 결정 및 일정 계획
```markdown
## 구현 우선순위 및 일정
### Phase 1: 설계 및 검증 (2일)
**날짜**: 2025-01-20 ~ 2025-01-21
- [x] 호환성 매트릭스 작성
- [x] Context 타입 설계
- [ ] 우선순위 결정
- [ ] package.json 초안
**결정사항**:
- DatePicker는 Phase 3에 구현 (date-fns 학습 필요)
- Slider는 Phase 4로 미룸 (P3 우선순위)
- Array/Object는 Phase 3에 포함 (MVP에 필요)
### Phase 2: 기본 인프라 (3일)
**예상 날짜**: 2025-01-22 ~ 2025-01-24
**Priority 1: 프로젝트 설정**
- [ ] yarn create 및 패키지 초기화
- [ ] tsconfig.json, rollup.config.js 설정
- [ ] @canard/schema-form peerDependencies 설정
- [ ] MUI 의존성 설치
**Priority 2: 타입 및 렌더러**
- [ ] src/type.ts 완성
- [ ] FormGroup, FormLabel, FormInput, FormError 구현
- [ ] formatError 헬퍼 함수
### Phase 3: 핵심 컴포넌트 (5일)
**예상 날짜**: 2025-01-25 ~ 2025-01-29
**Day 1-2: P1 기본 Input**
- [ ] FormTypeInputString
- [ ] FormTypeInputNumber
- [ ] FormTypeInputBoolean (Checkbox)
**Day 3-4: P2 특수 Format**
- [ ] FormTypeInputTextarea
- [ ] FormTypeInputPassword
- [ ] FormTypeInputEmail
**Day 5: Enum 및 구조**
- [ ] FormTypeInputStringEnum (Select 또는 RadioGroup)
- [ ] FormTypeInputArray (간단 버전)
- [ ] formTypeInputDefinitions 우선순위 정렬
### Phase 4: 고급 기능 (3일)
**예상 날짜**: 2025-01-30 ~ 2025-02-01
- [ ] FormTypeInputDate (DatePicker)
- [ ] FormTypeInputTime (TimePicker)
- [ ] README.md 작성
- [ ] Storybook stories 3-5개
### Phase 5: 최적화 (2일)
**예상 날짜**: 2025-02-02 ~ 2025-02-03
- [ ] 성능 최적화 체크리스트
- [ ] 접근성 테스트 (axe-core)
- [ ] 통합 테스트
- [ ] 빌드 및 배포
**총 예상 기간**: 15일 (3주)
```
#### Step 4: package.json 초안
```json
{
"name": "@canard/schema-form-mui-plugin",
"version": "0.1.0",
"description": "Material-UI (MUI) v6 plugin for @canard/schema-form",
"keywords": ["canard", "schema-form", "mui", "material-ui", "react", "form"],
"author": "Your Name",
"license": "MIT",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs.js",
"types": "./dist/index.d.ts"
}
},
"files": ["dist", "README.md"],
"scripts": {
"build": "rollup -c",
"typecheck": "tsc --noEmit",
"lint": "eslint src --ext .ts,.tsx",
"test": "vitest run",
"test:watch": "vitest"
},
"peerDependencies": {
"@canard/schema-form": "*",
"@mui/material": "^6.0.0",
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
},
"dependencies": {
"@winglet/react-utils": "*"
},
"devDependencies": {
"@canard/schema-form": "*",
"@mui/material": "^6.0.0",
"@types/react": "^18.0.0",
"react": "^18.0.0",
"rollup": "^4.0.0",
"typescript": "^5.0.0"
}
}
```
**Phase 1 완료 조건**:
- ✅ 호환성 매트릭스 완성
- ✅ Context 타입 설계 완료
- ✅ 우선순위 및 일정 결정
- ✅ package.json 초안 작성
**다음 단계**: Phase 2로 진행, 프로젝트 설정 시작
---
### 시나리오 2: Ant Design 플러그인 Phase 2 - 기본 인프라 구축
**상황**: Ant Design v5 플러그인, Phase 1 완료 후 Phase 2 진행
**단계**:
#### Step 1: 프로젝트 초기화
```bash
# 1. 패키지 디렉토리 생성
mkdir packages/canard-schema-form-antd-plugin
cd packages/canard-schema-form-antd-plugin
# 2. package.json 생성 (Phase 1 초안 기반)
yarn init -y
# 3. TypeScript 및 빌드 도구 설치
yarn add -D typescript rollup @rollup/plugin-typescript rollup-plugin-dts
# 4. Ant Design 및 canard-form 설치 (dev)
yarn add -D @canard/schema-form antd react react-dom @types/react
# 5. winglet utils 의존성 추가
yarn add @winglet/react-utils
# 6. tsconfig.json 생성
```
#### Step 2: tsconfig.json 설정
```json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM"],
"jsx": "react-jsx",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"removeComments": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": false
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"]
}
```
#### Step 3: src/type.ts 구현
```typescript
// src/type.ts
/**
* Ant Design 플러그인 Context 타입
*/
export interface AntdFormContext {
/**
* 전역 컴포넌트 크기
* @default 'middle'
*/
size?: 'small' | 'middle' | 'large';
/**
* 전역 Input variant (Ant Design은 variant 없음, bordered로 제어)
* @default true
*/
bordered?: boolean;
/**
* ConfigProvider의 componentSize 연동
*/
componentSize?: 'small' | 'middle' | 'large';
/**
* 파일 업로드 콜백 (Phase 4)
*/
onFileAttach?: (file: File) => Promise<string>;
/**
* 날짜 포맷 로케일
* @default 'ko_KR'
*/
locale?: string;
}
/**
* 재사용 타입
*/
export type { FormTypeInputPropsWithSchema } from '@canard/schema-form';
```
#### Step 4: 기본 렌더러 구현 (FormGroup)
```typescript
// src/components/FormGroup.tsx
import type { FormTypeRendererProps } from '@canard/schema-form';
import { Box } from 'antd'; // ⚠️ Ant Design에는 Box가 없음, div 사용
/**
* FormGroup: 폼 그룹 렌더러
* depth에 따라 들여쓰기 적용
*/
export const FormGroup = ({ depth, children }: FormTypeRendererProps) => {
return (
<div
style={{
paddingLeft: depth * 16,
borderLeft: depth > 0 ? '2px solid #f0f0f0' : undefined,
marginBottom: 16,
}}
>
{children}
</div>
);
};
```
#### Step 5: FormLabel 구현
```typescript
// src/components/FormLabel.tsx
import type { FormTypeRendererProps } from '@canard/schema-form';
/**
* FormLabel: 폼 라벨 렌더러
* Ant Design은 Form.Item이 라벨을 처리하므로 여기서는 간단히 구현
*/
export const FormLabel = ({ children, htmlFor, required }: FormTypeRendererProps) => {
return (
<label
htmlFor={htmlFor}
style={{
display: 'block',
marginBottom: 4,
fontSize: 14,
fontWeight: 500,
}}
>
{children}
{required && <span style={{ color: 'red', marginLeft: 4 }}>*</span>}
</label>
);
};
```
#### Step 6: FormInput, FormError, formatError 구현
```typescript
// src/components/FormInput.tsx
import type { FormTypeRendererProps } from '@canard/schema-form';
/**
* FormInput: 입력 필드 래퍼
* Ant Design은 Form.Item을 사용하지 않으므로 단순 wrapper
*/
export const FormInput = ({ children }: FormTypeRendererProps) => {
return <div style={{ marginBottom: 8 }}>{children}</div>;
};
// src/components/FormError.tsx
import type { FormTypeRendererProps } from '@canard/schema-form';
/**
* FormError: 에러 메시지 렌더러
*/
export const FormError = ({ children }: FormTypeRendererProps) => {
if (!children) return null;
return (
<div
style={{
color: '#ff4d4f',
fontSize: 12,
marginTop: 4,
}}
>
{children}
</div>
);
};
// src/components/formatError.ts
import type { FormTypeError } from '@canard/schema-form';
/**
* formatError: 에러 객체를 문자열로 변환
*/
export const formatError = (error: FormTypeError): string => {
// JSON Schema validation 에러
if (error.keyword) {
const keywordMessages: Record<string, string> = {
required: '필수 입력 항목입니다',
minLength: `최소 ${error.params?.limit}자 이상 입력해야 합니다`,
maxLength: `최대 ${error.params?.limit}자까지 입력 가능합니다`,
minimum: `최소값은 ${error.params?.limit}입니다`,
maximum: `최대값은 ${error.params?.limit}입니다`,
pattern: '형식이 올바르지 않습니다',
format: '형식이 올바르지 않습니다',
enum: '허용되지 않는 값입니다',
type: '타입이 올바르지 않습니다',
};
return keywordMessages[error.keyword] || error.message || '유효하지 않은 값입니다';
}
// 일반 에러
return error.message || '유효하지 않은 값입니다';
};
```
#### Step 7: src/index.ts 구현
```typescript
// src/index.ts
import type { SchemaFormPlugin } from '@canard/schema-form';
import { FormError } from './components/FormError';
import { FormGroup } from './components/FormGroup';
import { FormInput } from './components/FormInput';
import { FormLabel } from './components/FormLabel';
import { formatError } from './components/formatError';
/**
* Ant Design v5 플러그인
* Phase 2: 기본 인프라만 구현, formTypeInputDefinitions는 Phase 3에서 추가
*/
export const plugin = {
FormGroup,
FormLabel,
FormInput,
FormError,
formatError,
formTypeInputDefinitions: [], // ⚠️ Phase 3에서 구현
} satisfies SchemaFormPlugin;
export type * from './type';
```
#### Step 8: rollup.config.js 설정
```javascript
// rollup.config.js
import typescript from '@rollup/plugin-typescript';
import dts from 'rollup-plugin-dts';
export default [
// ESM 및 CJS 빌드
{
input: 'src/index.ts',
output: [
{
file: 'dist/index.esm.js',
format: 'esm',
sourcemap: true,
},
{
file: 'dist/index.cjs.js',
format: 'cjs',
sourcemap: true,
},
],
external: [
'react',
'react-dom',
'antd',
'@canard/schema-form',
'@winglet/react-utils',
],
plugins: [
typescript({
tsconfig: './tsconfig.json',
declaration: false, // dts 플러그인이 처리
}),
],
},
// 타입 선언 파일 번들
{
input: 'src/index.ts',
output: {
file: 'dist/index.d.ts',
format: 'esm',
},
external: [/\.css$/],
plugins: [dts()],
},
];
```
#### Step 9: 빌드 및 검증
```bash
# 1. TypeScript 타입 체크
yarn typecheck
# ✅ 에러 없이 통과해야 함
# 2. 빌드
yarn build
# ✅ dist/index.esm.js, dist/index.cjs.js, dist/index.d.ts 생성 확인
# 3. 생성된 파일 확인
ls -la dist/
# 출력:
# index.esm.js
# index.esm.js.map
# index.cjs.js
# index.cjs.js.map
# index.d.ts
```
**Phase 2 완료 조건**:
- ✅ 빌드 성공 (`yarn build`)
- ✅ 타입 체크 통과 (`yarn typecheck`)
- ✅ 기본 렌더러 5개 구현 완료 (FormGroup, FormLabel, FormInput, FormError, formatError)
- ✅ plugin 객체 export 성공
**다음 단계**: Phase 3로 진행, FormTypeInput 컴포넌트 구현 시작
---
### 시나리오 3: Chakra UI 플러그인 Phase 3 - 핵심 컴포넌트 구현
**상황**: Chakra UI v2 플러그인, Phase 2 완료 후 Phase 3 핵심 컴포넌트 구현
**단계**:
#### Day 1-2: Priority 1 기본 Input 구현
```typescript
// src/formTypeInputs/FormTypeInputString.tsx
import { useMemo } from 'react';
import { useHandle } from '@winglet/react-utils';
import type { FormTypeInputPropsWithSchema, FormTypeInputDefinition } from '@canard/schema-form';
import {
FormControl,
FormLabel,
Input,
FormErrorMessage,
FormHelperText,
} from '@chakra-ui/react';
import type { ChakraFormContext } from '../type';
interface FormTypeInputStringProps
extends FormTypeInputPropsWithSchema<string, never, ChakraFormContext>,
ChakraFormContext {}
const FormTypeInputString = ({
jsonSchema,
path,
name,
required,
disabled,
readOnly,
errors,
defaultValue,
onChange,
context,
size: sizeProp,
}: FormTypeInputStringProps) => {
const [size, label] = useMemo(() => {
return [
sizeProp ?? context.size ?? 'md',
jsonSchema.label ?? jsonSchema.title ?? name,
];
}, [sizeProp, context.size, jsonSchema.label, jsonSchema.title, name]);
const hasError = errors.length > 0;
const errorMessage = hasError ? errors[0].message : undefined;
const handleChange = useHandle((event: React.ChangeEvent<HTMLInputElement>) => {
onChange(event.target.value);
});
// ✅ Chakra UI는 FormControl로 감싸야 자동 접근성 처리
return (
<FormControl isRequired={required} isInvalid={hasError} isDisabled={disabled || readOnly}>
<FormLabel htmlFor={path}>{label}</FormLabel>
<Input
id={path}
name={name}
defaultValue={defaultValue ?? ''}
onChange={handleChange}
size={size}
placeholder={jsonSchema.placeholder}
/>
{hasError && <FormErrorMessage>{errorMessage}</FormErrorMessage>}
{!hasError && jsonSchema.description && (
<FormHelperText>{jsonSchema.description}</FormHelperText>
)}
</FormControl>
);
};
export const FormTypeInputStringDefinition = {
Component: FormTypeInputString,
test: { type: 'string' },
} satisfies FormTypeInputDefinition;
```
```typescript
// src/formTypeInputs/FormTypeInputNumber.tsx
const FormTypeInputNumber = ({ /* ...props */ }: FormTypeInputNumberProps) => {
// FormTypeInputString과 유사, Input에 type="number" 추가
return (
<FormControl isRequired={required} isInvalid={hasError}>
<FormLabel htmlFor={path}>{label}</FormLabel>
<Input
id={path}
name={name}
type="number" // ✅ 차이점
defaultValue={defaultValue ?? ''}
onChange={(e) => onChange(parseFloat(e.target.value) || 0)}
size={size}
/>
{/* ... */}
</FormControl>
);
};
export const FormTypeInputNumberDefinition = {
Component: FormTypeInputNumber,
test: (params) => {
return params.jsonSchema.type === 'number' || params.jsonSchema.type === 'integer';
},
} satisfies FormTypeInputDefinition;
```
```typescript
// src/formTypeInputs/FormTypeInputBoolean.tsx
import { Checkbox } from '@chakra-ui/react';
const FormTypeInputBoolean = ({ /* ...props */ }: FormTypeInputBooleanProps) => {
const handleChange = useHandle((event: React.ChangeEvent<HTMLInputElement>) => {
onChange(event.target.checked);
});
return (
<FormControl isDisabled={disabled || readOnly}>
<Checkbox
id={path}
name={name}
defaultChecked={defaultValue ?? false}
onChange={handleChange}
size={size}
>
{label}
</Checkbox>
{jsonSchema.description && (
<FormHelperText>{jsonSchema.description}</FormHelperText>
)}
</FormControl>
);
};
export const FormTypeInputBooleanDefinition = {
Component: FormTypeInputBoolean,
test: { type: 'boolean' },
} satisfies FormTypeInputDefinition;
```
**Day 1-2 완료**:
- ✅ FormTypeInputString
- ✅ FormTypeInputNumber
- ✅ FormTypeInputBoolean
#### Day 3-4: Priority 2 특수 Format 구현
```typescript
// src/formTypeInputs/FormTypeInputTextarea.tsx
import { Textarea } from '@chakra-ui/react';
const FormTypeInputTextarea = ({ /* ...props */ }: FormTypeInputTextareaProps) => {
return (
<FormControl isRequired={required} isInvalid={hasError}>
<FormLabel htm