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

10 KiB

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 인터페이스:

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 인터페이스:

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 인터페이스:

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 렌더러를 호출하도록 수정:

// 메타 컴포넌트는 자체 렌더러로 직접 렌더링
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를 전달하는 진정한 디스패처로 수정:

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: 전체 통합 브라우저 테스트