ERP-node/docs/v3-real-pipeline-plan.md

308 lines
10 KiB
Markdown
Raw Permalink Normal View History

2026-03-01 03:39:00 +09:00
# V3 메타 컴포넌트 - 자체 데이터 파이프라인 구축 계획
> **핵심 원칙**: V3 메타 컴포넌트는 V2를 래핑하지 않는다.
> 자체적으로 완전히 동작하는 렌더링 + 데이터 파이프라인을 가진다.
> V2의 좋은 부분(API, EventBus, formData 패턴)은 재사용하되, 렌더러는 새로 만든다.
---
## 아키텍처
```
[V3 메타 컴포넌트]
├─ FieldRenderer ─── formData/onFormDataChange + entityJoinApi + validation
├─ DataViewRenderer ─── entityJoinApi.getTableDataWithJoins + 페이징/정렬/필터
├─ ActionRenderer ─── ButtonActionExecutor + v2EventBus
├─ SearchRenderer ─── v2EventBus로 DataView 필터링
├─ LayoutRenderer ─── children 관리 (순수 컨테이너)
├─ DisplayRenderer ─── formData 바인딩 + 포맷팅
└─ ModalRenderer ─── 모달 상태 + 내부 폼
```
## 재사용하는 V2 인프라 (수정 없이 그대로 사용)
| 모듈 | 파일 | 용도 |
|------|------|------|
| entityJoinApi | `frontend/lib/api/entityJoin.ts` | 테이블 데이터 조회 (조인 포함) |
| DynamicFormApi | `frontend/lib/api/dynamicForm.ts` | 폼 저장/검증 |
| screenApi | `frontend/lib/api/screen.ts` | CRUD API (add/edit/delete) |
| v2EventBus | `frontend/lib/v2-core/events/EventBus.ts` | 컴포넌트 간 이벤트 통신 |
| V2_EVENTS | `frontend/lib/v2-core/events/types.ts` | 이벤트 타입 상수 |
| ButtonActionExecutor | `frontend/lib/utils/buttonActions.ts` | 버튼 액션 실행 |
| apiClient | `frontend/lib/api/client.ts` | HTTP 클라이언트 |
| formData 패턴 | props 전달 | `formData`, `onFormDataChange(fieldName, value)` |
## 새로 만드는 V3 전용 모듈
---
### Phase 1: FieldRenderer (자체 데이터 바인딩)
**목표**: 하나의 FieldRenderer가 webType에 따라 모든 입력 필드를 렌더링
**파일**: `frontend/lib/meta-components/Field/FieldRenderer.tsx`
**기능 요구사항**:
1. `formData[columnName]`에서 현재 값 읽기
2. `onFormDataChange(columnName, newValue)` 호출로 값 변경
3. webType별 렌더링:
- `text`, `number`, `email`, `tel`, `url`, `password`: shadcn `<Input>` 사용
- `textarea`: shadcn `<Textarea>` 사용
- `date`, `datetime`: shadcn date picker 또는 기존 `<input type="date">` 사용
- `select`: shadcn `<Select>` 사용, options는 config.options에서 가져오거나 API 호출
- `checkbox`, `radio`, `toggle`: shadcn `<Checkbox>`, `<RadioGroup>`, `<Switch>`
- `entity`: 엔티티 검색 입력 (autocomplete) - `entityJoinApi` 활용
- `file`: 파일 업로드 - 기존 file API 활용
- `numbering`: 읽기 전용 채번 필드
4. 라벨 표시 (config.label, labelPosition)
5. 필수/읽기전용/비활성 상태 처리
6. 검증 규칙 적용 (config.validation)
**Props 인터페이스**:
```typescript
interface FieldRendererProps {
id: string;
config: FieldComponentConfig;
// 데이터 바인딩 (상위에서 전달)
formData?: Record<string, any>;
onFormDataChange?: (fieldName: string, value: any) => void;
// 컨텍스트
tableName?: string;
companyCode?: string;
screenId?: number;
// UI 모드
isDesignMode?: boolean;
disabled?: boolean;
className?: string;
}
```
**shadcn 컴포넌트 사용**:
- Input, Textarea, Select, Checkbox, RadioGroup, Switch, Label, DatePicker
- 모두 기존 설치된 shadcn/ui 활용
**엔티티 검색 구현**:
- config.join이 있으면 엔티티 검색 모드
- entityJoinApi를 사용하여 검색 대상 테이블 조회
- Popover + Command (shadcn combobox 패턴) 사용
---
### Phase 2: DataViewRenderer (자체 데이터 Fetching)
**목표**: 하나의 DataViewRenderer가 테이블/카드/트리 뷰를 모두 처리
**파일**: `frontend/lib/meta-components/DataView/DataViewRenderer.tsx`
**기능 요구사항**:
1. 컴포넌트 마운트 시 `entityJoinApi.getTableDataWithJoins(config.tableName, ...)` 호출
2. 페이징 처리 (config.pageSize, 현재 페이지 상태 관리)
3. 정렬 (컬럼 헤더 클릭)
4. 행 선택 (단일/다중)
5. 행 클릭 시 formData에 선택된 행 데이터 반영
6. v2EventBus.subscribe(TABLE_REFRESH) 구독 → 데이터 재로드
7. v2EventBus.emitSync(TABLE_DATA_CHANGE) 발행 → 선택 변경 시
8. viewMode에 따라 테이블/카드 전환
**Props 인터페이스**:
```typescript
interface DataViewRendererProps {
id: string;
config: DataViewComponentConfig;
// 선택된 행
selectedRowsData?: any[];
onSelectedRowsChange?: (rows: any[], data: any[]) => void;
// 컨텍스트
tableName?: string;
companyCode?: string;
screenId?: number;
// formData (마스터-디테일 연동용)
formData?: Record<string, any>;
onFormDataChange?: (fieldName: string, value: any) => void;
// UI
isDesignMode?: boolean;
className?: string;
onRefresh?: () => void;
}
```
**테이블 렌더링**:
- shadcn `<Table>` 기반
- 컬럼은 config.columns 또는 API에서 컬럼 메타데이터 자동 조회
- 페이징: 하단에 페이지네이션 컴포넌트
- 정렬: 컬럼 헤더 클릭으로 ASC/DESC 전환
**디자인 모드**:
- 데이터 로드 안 함
- 컬럼 헤더만 표시 + 샘플 빈 행 표시
- "DataView: {tableName}" 배지 표시
---
### Phase 3: ActionRenderer (자체 CRUD 실행)
**목표**: 버튼 클릭 시 config.steps에 정의된 액션을 순차 실행
**파일**: `frontend/lib/meta-components/Action/ActionRenderer.tsx`
**기능 요구사항**:
1. shadcn `<Button>` 렌더링 (variant, icon, label)
2. 클릭 시 config.steps를 순차 실행:
- `save`: `screenApi.addTableData()` 또는 `screenApi.editTableData()` 호출
- `delete`: `screenApi.deleteTableData()` 호출
- `refresh`: `v2EventBus.emitSync(TABLE_REFRESH)` 발행
- `navigate`: router.push로 화면 이동
- `openModal`: 모달 열기 이벤트
- `api`: 커스텀 API 호출
- `toast`: 토스트 메시지 표시
- `validate`: 폼 검증
3. config.confirmDialog가 있으면 실행 전 확인 대화상자
4. config.enableCondition에 따라 버튼 활성/비활성
**Props 인터페이스**:
```typescript
interface ActionRendererProps {
id: string;
config: ActionComponentConfig;
// 데이터
formData?: Record<string, any>;
selectedRowsData?: any[];
// 컨텍스트
tableName?: string;
companyCode?: string;
screenId?: number;
userId?: string;
// 콜백
onRefresh?: () => void;
// UI
isDesignMode?: boolean;
disabled?: boolean;
className?: string;
}
```
---
### Phase 4: SearchRenderer, LayoutRenderer, DisplayRenderer, ModalRenderer
#### SearchRenderer
- 검색 필드들 렌더링 (config.fields 기반)
- "검색" 버튼 클릭 시 필터 조건 구성
- v2EventBus로 연결된 DataView에 필터 전달
- 또는 onSearch 콜백으로 필터 전달
#### LayoutRenderer
- config.mode에 따라 레이아웃 구성:
- `columns`: CSS Grid 기반 수평 분할
- `rows`: Flex 기반 수직 분할
- `tabs`: shadcn `<Tabs>` 사용
- `card`: shadcn `<Card>` 사용
- `accordion`: shadcn `<Accordion>` 사용
- children을 각 영역에 배치
#### DisplayRenderer
- config.displayType에 따라 표시:
- `text`/`heading`: 텍스트/제목 표시
- `divider`: 구분선
- `badge`: 뱃지
- `alert`: 알림
- `stat`: 통계 카드
- config.dataBinding이 있으면 formData에서 값 바인딩
#### ModalRenderer
- 트리거 버튼 렌더링
- 클릭 시 shadcn `<Dialog>` 열기
- 모달 내부에 폼 필드 자동 구성 (config.content.formConfig 기반)
- 저장/취소 버튼
---
## RealtimePreviewDynamic.tsx 수정
메타 컴포넌트 렌더링 시 `metaToV2` 변환 없이 직접 V3 렌더러를 호출하도록 수정:
```typescript
// 메타 컴포넌트는 자체 렌더러로 직접 렌더링
if (isMetaComponent(component)) {
return (
<MetaComponentRenderer
component={{ id, type: componentType, config: componentConfig }}
formData={formData}
onFormDataChange={onFormDataChange}
selectedRowsData={selectedRowsData}
onSelectedRowsChange={onSelectedRowsChange}
tableName={tableName}
companyCode={companyCode}
screenId={screenId}
userId={userId}
onRefresh={onRefresh}
isDesignMode={isDesignMode}
/>
);
}
// V2 컴포넌트는 기존 DynamicComponentRenderer로
return <DynamicComponentRenderer ... />;
```
## MetaComponentRenderer.tsx 수정
각 개별 렌더러에 모든 필요한 props를 전달하는 진정한 디스패처로 수정:
```typescript
export function MetaComponentRenderer({ component, ...runtimeProps }) {
const { type, config } = component;
switch (type) {
case "meta-field":
return <FieldRenderer id={component.id} config={config} {...runtimeProps} />;
case "meta-dataview":
return <DataViewRenderer id={component.id} config={config} {...runtimeProps} />;
case "meta-action":
return <ActionRenderer id={component.id} config={config} {...runtimeProps} />;
// ...
}
}
```
---
## 테스트 기준
### Phase 1 (FieldRenderer) 완료 조건:
- [ ] text/number/email 입력 → formData에 값 반영
- [ ] select → 옵션 목록 표시, 선택 시 formData 반영
- [ ] date → 날짜 선택 가능
- [ ] 라벨, 필수 표시, 읽기전용 동작
- [ ] 디자인 모드에서 비활성 상태로 표시
### Phase 2 (DataViewRenderer) 완료 조건:
- [ ] 테이블명으로 데이터 자동 로드
- [ ] 테이블 형태로 데이터 표시
- [ ] 페이징 동작
- [ ] 행 클릭 시 선택
- [ ] TABLE_REFRESH 이벤트에 반응
### Phase 3 (ActionRenderer) 완료 조건:
- [ ] 버튼 렌더링 (variant, icon)
- [ ] 클릭 시 save → API 호출 → 토스트
- [ ] 클릭 시 delete → 확인 → API 호출
- [ ] 클릭 시 refresh → 테이블 새로고침
### 전체 통합 테스트:
- [ ] V3 변환 후 입력 필드에 데이터 입력 가능
- [ ] V3 변환 후 테이블에 데이터 로드됨
- [ ] V3 변환 후 버튼 클릭 시 저장/삭제 동작
- [ ] TypeScript 빌드 에러 0개
---
## 실행 순서
1. **frontend 에이전트**: Phase 1 (FieldRenderer) 구현 + 빌드 테스트
2. **frontend 에이전트**: Phase 2 (DataViewRenderer) 구현 + 빌드 테스트
3. **frontend 에이전트**: Phase 3 (ActionRenderer) 구현 + 빌드 테스트
4. **frontend 에이전트**: Phase 4 (나머지) 구현 + 빌드 테스트
5. **frontend 에이전트**: RealtimePreviewDynamic + MetaComponentRenderer 수정
6. **browser-use**: 전체 통합 브라우저 테스트