- Deleted the following files as they are no longer relevant to the current project structure: - 결재 시스템 구현 현황 - 결재 시스템 v2 사용 가이드 - WACE 시스템 문제점 분석 및 개선 계획 - Agent Pipeline 한계점 분석 - AI 기반 화면 자동 생성 시스템 설계서 - WACE ERP Backend - 분석 문서 인덱스 These deletions help streamline the documentation and remove obsolete information, ensuring that only current and relevant resources are maintained.
41 KiB
V2 컴포넌트 분석 가이드
개요
V2 컴포넌트는 화면관리 시스템 전용으로 개발된 컴포넌트 세트입니다. 기존 컴포넌트와의 충돌을 방지하고, 새로운 기능(엔티티 조인, 다국어 지원, 커스텀 테이블 등)을 지원합니다.
핵심 원칙
- 모든 V2 컴포넌트는
v2-접두사를 사용 - 원본 컴포넌트는 기존 화면 호환성 유지용으로 보존
- 새로운 화면 개발 시 반드시 V2 컴포넌트만 사용
- Definition 이름에
V2접두사 사용 (예:V2TableListDefinition)
파일 경로
frontend/lib/registry/components/
├── v2-button-primary/ ← V2 컴포넌트 (수정 대상)
├── v2-table-list/ ← V2 컴포넌트 (수정 대상)
├── v2-split-panel-layout/ ← V2 컴포넌트 (수정 대상)
├── ...
├── button-primary/ ← 원본 (수정 금지)
├── table-list/ ← 원본 (수정 금지)
└── ...
V2 컴포넌트 목록 (18개)
| 컴포넌트 ID | 이름 | 카테고리 | 용도 |
|---|---|---|---|
v2-table-list |
테이블 리스트 | DISPLAY | 데이터 목록 표시 (테이블/카드 모드) |
v2-table-grouped |
그룹화 테이블 | DISPLAY | NEW 그룹별 접기/펼치기 테이블 |
v2-split-panel-layout |
분할 패널 | DISPLAY | 마스터-디테일 레이아웃 |
v2-unified-repeater |
통합 리피터 | UNIFIED | 반복 데이터 관리 (인라인/모달/버튼) |
v2-pivot-grid |
피벗 그리드 | DISPLAY | 다차원 데이터 분석 피벗 테이블 |
v2-button-primary |
기본 버튼 | ACTION | 저장/삭제 등 액션 버튼 |
v2-text-display |
텍스트 표시 | DISPLAY | 텍스트/라벨 표시 |
v2-divider-line |
구분선 | DISPLAY | 시각적 구분선 |
v2-card-display |
카드 디스플레이 | DISPLAY | 카드 형태 데이터 표시 |
v2-numbering-rule |
채번 규칙 | DISPLAY | 코드 자동 채번 설정 |
v2-table-search-widget |
검색 필터 | DISPLAY | 테이블 검색/필터 위젯 |
v2-section-paper |
섹션 페이퍼 | LAYOUT | 섹션 구분 컨테이너 |
v2-section-card |
섹션 카드 | LAYOUT | 카드형 섹션 컨테이너 |
v2-tabs-widget |
탭 위젯 | LAYOUT | 탭 기반 콘텐츠 전환 |
v2-location-swap-selector |
위치 선택 | INPUT | 출발지/도착지 스왑 선택 |
v2-rack-structure |
렉 구조 | DISPLAY | 창고 렉 시각화 |
v2-aggregation-widget |
집계 위젯 | DISPLAY | 데이터 집계 (합계/평균/개수) |
v2-repeat-container |
리피터 컨테이너 | LAYOUT | 데이터 수만큼 반복 렌더링 |
주요 컴포넌트 상세 분석
1. v2-table-list (테이블 리스트)
용도: 데이터베이스 테이블 데이터를 테이블/카드 형태로 표시
주요 특징
- 테이블 모드 / 카드 모드 전환 가능
- 페이지네이션, 정렬, 필터링 지원
- 체크박스 선택 (단일/다중)
- 가로 스크롤 및 컬럼 고정
- 엔티티 조인 컬럼 지원
- 인라인 편집 기능
- Excel 내보내기
데이터 흐름
┌─────────────────────────────────────────────────────────────┐
│ v2-table-list │
├─────────────────────────────────────────────────────────────┤
│ ① config.selectedTable / customTableName 확인 │
│ ↓ │
│ ② tableTypeApi.getData() 호출 │
│ ↓ │
│ ③ entityJoinApi.getEntityJoinColumns() 조인 컬럼 로드 │
│ ↓ │
│ ④ 데이터 + 조인 데이터 병합 │
│ ↓ │
│ ⑤ 테이블/카드 모드로 렌더링 │
│ ↓ │
│ ⑥ onRowClick / onSelectionChange 이벤트 발생 │
└─────────────────────────────────────────────────────────────┘
주요 설정 인터페이스
interface TableListConfig {
// 표시 모드
displayMode: "table" | "card";
// 커스텀 테이블 설정
customTableName?: string; // 커스텀 테이블
useCustomTable?: boolean; // 커스텀 테이블 사용 여부
isReadOnly?: boolean; // 읽기전용
// 컬럼 설정
columns: ColumnConfig[];
// 페이지네이션
pagination: {
enabled: boolean;
pageSize: number;
showSizeSelector: boolean;
pageSizeOptions: number[];
};
// 체크박스
checkbox: {
enabled: boolean;
multiple: boolean; // true: 체크박스, false: 라디오
position: "left" | "right";
selectAll: boolean;
};
// 필터
filter: {
enabled: boolean;
filters: FilterConfig[];
};
// 연결된 필터 (다른 컴포넌트 값으로 필터링)
linkedFilters?: LinkedFilterConfig[];
// 제외 필터 (다른 테이블에 존재하는 데이터 제외)
excludeFilter?: ExcludeFilterConfig;
// 가로 스크롤 설정
horizontalScroll: {
enabled: boolean;
maxVisibleColumns?: number;
minColumnWidth?: number;
maxColumnWidth?: number;
};
}
컬럼 설정
interface ColumnConfig {
columnName: string; // 컬럼명
displayName: string; // 표시명
visible: boolean; // 표시 여부
sortable: boolean; // 정렬 가능
searchable: boolean; // 검색 가능
width?: number; // 너비
align: "left" | "center" | "right"; // 정렬
format?: "text" | "number" | "date" | "currency" | "boolean";
// 엔티티 조인
isEntityJoin?: boolean; // 조인 컬럼 여부
entityJoinInfo?: {
sourceTable: string;
sourceColumn: string;
joinAlias: string;
};
// 컬럼 고정
fixed?: "left" | "right" | false;
// 자동생성
autoGeneration?: {
type: "uuid" | "current_user" | "current_time" | "sequence" | "numbering_rule";
enabled: boolean;
};
// 편집 가능 여부
editable?: boolean;
}
2. v2-split-panel-layout (분할 패널)
용도: 마스터-디테일 패턴의 좌우 분할 레이아웃
주요 특징
- 좌측: 마스터 목록 (리스트/테이블/커스텀 모드)
- 우측: 디테일 정보 (연관 데이터)
- 좌우 비율 조절 가능 (드래그 리사이즈)
- 다중 탭 지원 (우측 패널)
- N:M 관계 데이터 지원
- 중복 제거 기능
- 커스텀 모드 (NEW): 패널 내부에 자유롭게 컴포넌트 배치
커스텀 모드 (displayMode: "custom")
패널 내부에 다양한 컴포넌트를 자유롭게 배치할 수 있는 모드입니다. v2-tabs-widget과 동일한 구조로 구현되어 있습니다.
특징:
- 패널 내부에 버튼, 테이블, 입력 필드 등 모든 V2 컴포넌트 배치 가능
- 드래그앤드롭으로 컴포넌트 이동
- 리사이즈 핸들로 크기 조절
- 컴포넌트 클릭 시 좌측 설정 패널에서 해당 컴포넌트 속성 편집
- 디자인 모드에서 실제 컴포넌트가 렌더링되어 미리보기 가능
사용 방법:
- 좌측/우측 패널의
displayMode를"custom"으로 설정 - 컴포넌트 팔레트에서 원하는 컴포넌트를 패널로 드래그
- 배치된 컴포넌트 클릭 → 좌측 패널에서 속성 편집
- 드래그 핸들(상단)로 이동, 모서리 핸들로 크기 조절
컴포넌트 구조:
interface PanelInlineComponent {
id: string;
componentType: string;
label?: string;
position?: { x: number; y: number };
size?: { width: number; height: number };
componentConfig?: Record<string, any>;
style?: Record<string, any>;
}
데이터 흐름
┌─────────────────────────────────────────────────────────────┐
│ v2-split-panel-layout │
├─────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 좌측 패널 │ ───────→ │ 우측 패널 │ │
│ │ (마스터) │ 선택 이벤트│ (디테일) │ │
│ └──────────────┘ └──────────────────────┘ │
│ │ │ │
│ ↓ ↓ │
│ leftPanel.tableName rightPanel.tableName │
│ leftPanel.columns rightPanel.relation │
│ │ │ │
│ ↓ ↓ │
│ 좌측 데이터 조회 ─────────→ 관계 설정에 따라 우측 필터링 │
│ (독립 API 호출) (FK/조인 키 기반) │
└─────────────────────────────────────────────────────────────┘
주요 설정 인터페이스
interface SplitPanelLayoutConfig {
// 좌측 패널
leftPanel: {
title: string;
tableName?: string;
useCustomTable?: boolean;
customTableName?: string;
displayMode?: "list" | "table" | "custom"; // 🆕 커스텀 모드 추가
showSearch?: boolean;
showAdd?: boolean;
showEdit?: boolean;
showDelete?: boolean;
columns?: ColumnConfig[];
tableConfig?: TableDisplayConfig;
dataFilter?: DataFilterConfig;
// 🆕 커스텀 모드 전용: 내부 컴포넌트 배열
components?: PanelInlineComponent[];
};
// 우측 패널
rightPanel: {
title: string;
tableName?: string;
displayMode?: "list" | "table" | "custom"; // 🆕 커스텀 모드 추가
columns?: ColumnConfig[];
// 🆕 커스텀 모드 전용: 내부 컴포넌트 배열
components?: PanelInlineComponent[];
// 관계 설정
relation?: {
type?: "join" | "detail";
leftColumn?: string; // 좌측 조인 컬럼
rightColumn?: string; // 우측 조인 컬럼
foreignKey?: string; // FK 컬럼
keys?: Array<{ // 복합키 지원
leftColumn: string;
rightColumn: string;
}>;
};
// 추가 설정 (N:M 관계)
addConfig?: {
targetTable?: string; // 실제 INSERT 테이블
autoFillColumns?: Record<string, any>;
leftPanelColumn?: string;
targetColumn?: string;
};
// 중복 제거
deduplication?: {
enabled: boolean;
groupByColumn: string;
keepStrategy: "latest" | "earliest" | "base_price" | "current_date";
};
// 추가 탭
additionalTabs?: AdditionalTabConfig[];
};
// 레이아웃
splitRatio?: number; // 좌우 비율 (0-100)
resizable?: boolean; // 크기 조절 가능
minLeftWidth?: number;
minRightWidth?: number;
// 동작
autoLoad?: boolean;
syncSelection?: boolean;
}
3. v2-unified-repeater (통합 리피터)
용도: 반복 데이터 관리 (기존 여러 리피터 통합)
주요 특징
- 3가지 렌더링 모드: 인라인/모달/버튼
- 마스터-디테일 FK 자동 연결
- 저장 테이블 분리 가능
- 행 추가/삭제, 드래그 정렬
- 선택 기능 (단일/다중)
데이터 흐름
┌─────────────────────────────────────────────────────────────┐
│ v2-unified-repeater │
├─────────────────────────────────────────────────────────────┤
│ ① 마스터 저장 이벤트 수신 (repeaterSave) │
│ ↓ │
│ ② masterRecordId 전달받음 │
│ ↓ │
│ ③ foreignKeyColumn에 masterRecordId 자동 설정 │
│ ↓ │
│ ④ dataSource.tableName으로 데이터 저장 │
│ ↓ │
│ ⑤ 저장 완료 후 onDataChange 이벤트 발생 │
└─────────────────────────────────────────────────────────────┘
주요 설정 인터페이스
interface UnifiedRepeaterConfig {
// 렌더링 모드
renderMode: "inline" | "modal" | "button" | "mixed";
// 데이터 소스
dataSource: {
tableName: string; // 저장 테이블
foreignKey: string; // FK 컬럼
referenceKey: string; // 참조할 PK 컬럼
};
// 컬럼 설정
columns: ColumnConfig[];
// 모달 설정
modal: {
size: "sm" | "md" | "lg" | "xl";
};
// 버튼 설정
button: {
sourceType: "manual" | "auto";
manualButtons: ButtonConfig[];
layout: "horizontal" | "vertical";
style: "outline" | "solid";
};
// 기능 설정
features: {
showAddButton: boolean;
showDeleteButton: boolean;
inlineEdit: boolean;
dragSort: boolean;
showRowNumber: boolean;
selectable: boolean;
multiSelect: boolean;
};
}
데이터 전달 인터페이스
v2-unified-repeater는 DataProvidable과 DataReceivable 인터페이스를 구현하여 다른 컴포넌트와 데이터를 주고받을 수 있습니다.
DataProvidable 구현:
// 다른 컴포넌트에서 이 리피터의 데이터를 가져갈 수 있음
const dataProvider: DataProvidable = {
componentId: parentId || config.fieldName || "unified-repeater",
componentType: "unified-repeater",
// 선택된 행 데이터 반환
getSelectedData: () => {
return Array.from(selectedRows).map((idx) => data[idx]).filter(Boolean);
},
// 전체 데이터 반환
getAllData: () => {
return [...data];
},
// 선택 초기화
clearSelection: () => {
setSelectedRows(new Set());
},
};
DataReceivable 구현:
// 외부에서 이 리피터로 데이터를 전달받을 수 있음
const dataReceiver: DataReceivable = {
componentId: parentId || config.fieldName || "unified-repeater",
componentType: "repeater",
// 데이터 수신 (append, replace, merge 모드 지원)
receiveData: async (incomingData: any[], config: DataReceiverConfig) => {
// 매핑 규칙 적용 후 모드에 따라 처리
switch (config.mode) {
case "replace": setData(mappedData); break;
case "merge": /* 중복 제거 후 병합 */ break;
case "append": /* 기존 데이터에 추가 */ break;
}
},
// 현재 데이터 반환
getData: () => [...data],
};
ScreenContext 자동 등록:
// 컴포넌트 마운트 시 ScreenContext에 자동 등록
useEffect(() => {
if (screenContext && componentId) {
screenContext.registerDataProvider(componentId, dataProvider);
screenContext.registerDataReceiver(componentId, dataReceiver);
return () => {
screenContext.unregisterDataProvider(componentId);
screenContext.unregisterDataReceiver(componentId);
};
}
}, [screenContext, componentId]);
V2 이벤트 시스템
발행 이벤트:
| 이벤트 | 발행 시점 | 데이터 |
|---|---|---|
repeaterDataChange |
데이터 변경 시 | { componentId, tableName, data, selectedData } |
// 데이터 변경 시 V2 표준 이벤트 발행
import { V2_EVENTS, dispatchV2Event } from "@/types/component-events";
useEffect(() => {
if (data.length !== prevDataLengthRef.current) {
dispatchV2Event(V2_EVENTS.REPEATER_DATA_CHANGE, {
componentId: parentId || config.fieldName || "unified-repeater",
tableName: config.dataSource?.tableName || "",
data: data,
selectedData: Array.from(selectedRows).map((idx) => data[idx]).filter(Boolean),
});
}
}, [data, selectedRows]);
구독 이벤트:
| 이벤트 | 용도 |
|---|---|
beforeFormSave |
저장 전 데이터 수집 |
repeaterSave |
마스터 저장 후 FK 설정 |
componentDataTransfer |
컴포넌트 간 데이터 전달 수신 |
splitPanelDataTransfer |
분할 패널 간 데이터 전달 수신 |
4. v2-pivot-grid (피벗 그리드)
용도: 다차원 데이터 분석용 피벗 테이블
주요 특징
- 행/열/데이터/필터 영역 드래그앤드롭
- 다양한 집계 함수 (합계, 평균, 개수, 최대, 최소, 고유 개수)
- 소계/총계 표시 (위치 설정 가능)
- 조건부 서식 (색상 스케일, 데이터 바, 아이콘)
- 차트 연동
- Excel 내보내기
- 날짜 그룹화 (연/분기/월/주/일)
데이터 흐름
┌─────────────────────────────────────────────────────────────┐
│ v2-pivot-grid │
├─────────────────────────────────────────────────────────────┤
│ ① dataSource 설정 (테이블/API/정적 데이터) │
│ ↓ │
│ ② fields 설정 (행/열/데이터 필드 배치) │
│ ↓ │
│ ③ processPivotData() 로 피벗 계산 │
│ ↓ │
│ ④ 집계 함수 적용 (sum, avg, count 등) │
│ ↓ │
│ ⑤ PivotResult 생성 (rowHeaders, columnHeaders, dataMatrix)│
│ ↓ │
│ ⑥ 조건부 서식 적용 후 렌더링 │
└─────────────────────────────────────────────────────────────┘
주요 설정 인터페이스
interface PivotGridComponentConfig {
// 데이터 소스
dataSource?: {
type: "table" | "api" | "static";
tableName?: string;
apiEndpoint?: string;
staticData?: any[];
filterConditions?: FilterCondition[];
joinConfigs?: JoinConfig[];
};
// 필드 설정
fields?: Array<{
field: string; // 데이터 필드명
caption: string; // 표시 라벨
area: "row" | "column" | "data" | "filter";
areaIndex?: number; // 영역 내 순서
// 집계 (data 영역용)
summaryType?: "sum" | "count" | "avg" | "min" | "max" | "countDistinct";
// 날짜 그룹화
groupInterval?: "year" | "quarter" | "month" | "week" | "day";
// 포맷
format?: {
type: "number" | "currency" | "percent" | "date" | "text";
precision?: number;
thousandSeparator?: boolean;
prefix?: string;
suffix?: string;
};
}>;
// 총합계 설정
totals?: {
showRowGrandTotals?: boolean;
showRowTotals?: boolean;
showColumnGrandTotals?: boolean;
showColumnTotals?: boolean;
rowGrandTotalPosition?: "top" | "bottom";
columnGrandTotalPosition?: "left" | "right";
};
// 스타일
style?: {
theme: "default" | "compact" | "modern";
alternateRowColors?: boolean;
highlightTotals?: boolean;
conditionalFormats?: ConditionalFormatRule[];
};
// 필드 선택기
fieldChooser?: {
enabled: boolean;
allowSearch?: boolean;
};
// 차트 연동
chart?: {
enabled: boolean;
type: "bar" | "line" | "area" | "pie" | "stackedBar";
position: "top" | "bottom" | "left" | "right";
};
}
5. v2-aggregation-widget (집계 위젯)
용도: 데이터 집계 결과 표시 (합계, 평균, 개수 등)
주요 특징
- 다양한 집계 타입 (SUM, AVG, COUNT, MIN, MAX)
- 필터링 지원 (폼 데이터 연동)
- 가로/세로 레이아웃
- 아이콘 표시
- 폼 변경 시 자동 새로고침
데이터 흐름
┌─────────────────────────────────────────────────────────────┐
│ v2-aggregation-widget │
├─────────────────────────────────────────────────────────────┤
│ ① dataSourceType 확인 (table / repeater) │
│ ↓ │
│ ② filters 적용 (필터 조건 구성) │
│ ↓ │
│ ③ items 순회하며 각 집계 함수 실행 │
│ ↓ │
│ ④ 집계 결과 포맷팅 (천단위 구분, 접두사/접미사) │
│ ↓ │
│ ⑤ layout에 따라 렌더링 (horizontal / vertical) │
└─────────────────────────────────────────────────────────────┘
주요 설정 인터페이스
interface AggregationWidgetConfig {
// 데이터 소스
dataSourceType: "table" | "repeater";
// 집계 항목
items: Array<{
id: string;
label: string;
columnName: string;
aggregationType: "sum" | "avg" | "count" | "min" | "max";
format?: {
prefix?: string;
suffix?: string;
thousandSeparator?: boolean;
decimalPlaces?: number;
};
icon?: string;
color?: string;
}>;
// 필터 조건
filters: Array<{
column: string;
operator: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE" | "IN";
value?: any;
valueSource?: "static" | "formData" | "url";
valueField?: string;
}>;
filterLogic: "AND" | "OR";
// 레이아웃
layout: "horizontal" | "vertical";
gap: string;
// 스타일
showLabels: boolean;
showIcons: boolean;
backgroundColor: string;
borderRadius: string;
padding: string;
// 동작
autoRefresh: boolean;
refreshOnFormChange: boolean;
}
V2 이벤트 시스템
v2-aggregation-widget은 V2 표준 이벤트 시스템을 사용하여 다른 컴포넌트의 데이터 변경을 감지합니다.
구독 이벤트:
| 이벤트 | 용도 | 발행자 |
|---|---|---|
tableListDataChange |
테이블 데이터 변경 시 집계 갱신 | v2-table-list |
repeaterDataChange |
리피터 데이터 변경 시 집계 갱신 | v2-unified-repeater |
import { V2_EVENTS, subscribeV2Event, type TableListDataChangeDetail, type RepeaterDataChangeDetail } from "@/types/component-events";
useEffect(() => {
// 테이블 리스트 데이터 변경 이벤트 구독
const unsubscribeTableList = subscribeV2Event(
V2_EVENTS.TABLE_LIST_DATA_CHANGE,
(event: CustomEvent<TableListDataChangeDetail>) => {
const { data } = event.detail;
// 필터 적용 후 집계 재계산
const filteredData = applyFilters(data, filters, filterLogic, formData, selectedRows);
setData(filteredData);
}
);
// 리피터 데이터 변경 이벤트 구독
const unsubscribeRepeater = subscribeV2Event(
V2_EVENTS.REPEATER_DATA_CHANGE,
(event: CustomEvent<RepeaterDataChangeDetail>) => {
const { data, selectedData } = event.detail;
const rows = selectedData || data || [];
const filteredData = applyFilters(rows, filters, filterLogic, formData, selectedRows);
setData(filteredData);
}
);
return () => {
unsubscribeTableList();
unsubscribeRepeater();
};
}, [dataSourceType, isDesignMode, filterLogic]);
참고: 이전에 사용하던 중복 이벤트(selectionChange, tableSelectionChange, rowSelectionChange 등)는 제거되었습니다. V2 표준 이벤트만 사용합니다.
6. v2-table-search-widget (검색 필터)
용도: 테이블 데이터 검색 및 필터링
주요 특징
- 동적/고정 필터 모드
- 다중 테이블 지원
- 탭별 필터 값 저장
- 텍스트/숫자/날짜/셀렉트 필터 타입
- 다중선택 지원
- 대상 패널 지정 가능
데이터 흐름
┌─────────────────────────────────────────────────────────────┐
│ v2-table-search-widget │
├─────────────────────────────────────────────────────────────┤
│ ① TableOptionsContext에서 등록된 테이블 목록 조회 │
│ ↓ │
│ ② targetPanelPosition에 따라 대상 테이블 필터링 │
│ ↓ │
│ ③ 활성 필터 목록 로드 (localStorage에서 복원) │
│ ↓ │
│ ④ 필터 값 입력 → handleFilterChange() │
│ ↓ │
│ ⑤ currentTable.onFilterChange(filters) 호출 │
│ ↓ │
│ ⑥ 연결된 테이블이 자동으로 데이터 재조회 │
└─────────────────────────────────────────────────────────────┘
주요 설정 인터페이스
interface TableSearchWidgetConfig {
// 자동 선택
autoSelectFirstTable?: boolean;
showTableSelector?: boolean;
// 필터 모드
filterMode?: "dynamic" | "preset";
// 고정 필터 (preset 모드)
presetFilters?: Array<{
id: string;
columnName: string;
columnLabel: string;
filterType: "text" | "number" | "date" | "select";
width?: number;
multiSelect?: boolean;
}>;
// 대상 패널 위치
targetPanelPosition?: "left" | "right" | "auto";
}
7. v2-repeat-container (리피터 컨테이너)
용도: 데이터 수만큼 내부 컴포넌트를 반복 렌더링
주요 특징
- 수동/테이블/리피터 데이터 소스
- 세로/가로/그리드 레이아웃
- 페이징 지원
- 클릭 이벤트 (단일/다중 선택)
- 아이템 제목 템플릿
데이터 흐름
┌─────────────────────────────────────────────────────────────┐
│ v2-repeat-container │
├─────────────────────────────────────────────────────────────┤
│ ① dataSourceType에 따라 데이터 로드 │
│ - manual: 수동 입력 데이터 │
│ - table: DB 테이블에서 조회 │
│ - repeater: 리피터 컴포넌트 데이터 │
│ ↓ │
│ ② layout에 따라 배치 (vertical / horizontal / grid) │
│ ↓ │
│ ③ 각 아이템에 대해 children 렌더링 │
│ (RepeatItemContext로 현재 아이템 데이터 전달) │
│ ↓ │
│ ④ 클릭 시 선택 상태 관리 (selectionMode: single/multi) │
└─────────────────────────────────────────────────────────────┘
주요 설정 인터페이스
interface RepeatContainerConfig {
// 데이터 소스
dataSourceType: "manual" | "table" | "repeater";
tableName?: string;
repeaterComponentId?: string;
manualData?: any[];
// 레이아웃
layout: "vertical" | "horizontal" | "grid";
gridColumns: number;
gap: string;
// 스타일
showBorder: boolean;
showShadow: boolean;
borderRadius: string;
backgroundColor: string;
padding: string;
// 아이템 제목
showItemTitle: boolean;
itemTitleTemplate: string; // 예: "${name} - ${code}"
titleFontSize: string;
titleColor: string;
titleFontWeight: string;
// 빈 상태
emptyMessage: string;
// 페이징
usePaging: boolean;
pageSize: number;
// 선택
clickable: boolean;
showSelectedState: boolean;
selectionMode: "single" | "multi";
}
V2 이벤트 시스템
v2-repeat-container는 V2 표준 이벤트 시스템을 사용하여 다른 컴포넌트의 데이터 변경을 감지하고 반복 렌더링합니다.
구독 이벤트:
| 이벤트 | 용도 | 발행자 |
|---|---|---|
tableListDataChange |
테이블 데이터 변경 시 반복 항목 갱신 | v2-table-list |
repeaterDataChange |
리피터 데이터 변경 시 반복 항목 갱신 | v2-unified-repeater |
import { V2_EVENTS, subscribeV2Event, type TableListDataChangeDetail, type RepeaterDataChangeDetail } from "@/types/component-events";
useEffect(() => {
// 공통 데이터 처리 함수
const processIncomingData = (componentId: string | undefined, tableName: string | undefined, eventData: any[]) => {
// dataSourceComponentId가 설정된 경우 해당 컴포넌트만 매칭
if (dataSourceComponentId && componentId === dataSourceComponentId) {
setData(eventData);
setCurrentPage(1);
setSelectedIndices([]);
}
// 테이블명으로 매칭
else if (effectiveTableName && tableName === effectiveTableName) {
setData(eventData);
setCurrentPage(1);
setSelectedIndices([]);
}
};
// V2 표준 이벤트 구독
const unsubscribeTableList = subscribeV2Event(V2_EVENTS.TABLE_LIST_DATA_CHANGE, (event) => {
const { componentId, tableName, data } = event.detail;
processIncomingData(componentId, tableName, data);
});
const unsubscribeRepeater = subscribeV2Event(V2_EVENTS.REPEATER_DATA_CHANGE, (event) => {
const { componentId, tableName, data } = event.detail;
processIncomingData(componentId, tableName, data);
});
return () => {
unsubscribeTableList();
unsubscribeRepeater();
};
}, [dataSourceComponentId, effectiveTableName, isDesignMode]);
공통 데이터 흐름 패턴
1. 엔티티 조인 데이터 로드
모든 테이블 기반 V2 컴포넌트는 엔티티 조인을 지원합니다.
┌─────────────────────────────────────────────────────────────┐
│ 엔티티 조인 흐름 │
├─────────────────────────────────────────────────────────────┤
│ ① entityJoinApi.getEntityJoinColumns(tableName) │
│ ↓ │
│ ② 응답: { joinTables, availableColumns } │
│ ↓ │
│ ③ ConfigPanel에서 조인 컬럼 선택 │
│ ↓ │
│ ④ entityJoinApi.getTableDataWithJoins() 데이터 조회 │
│ ↓ │
│ ⑤ "테이블명.컬럼명" 형식으로 조인 데이터 포함 │
└─────────────────────────────────────────────────────────────┘
2. 폼 데이터 관리
V2 컴포넌트는 통합 폼 시스템(UnifiedFormContext)을 사용합니다.
┌─────────────────────────────────────────────────────────────┐
│ 폼 데이터 흐름 │
├─────────────────────────────────────────────────────────────┤
│ ① 컴포넌트에서 useFormCompatibility() 훅 사용 │
│ ↓ │
│ ② getValue(fieldName) - 값 읽기 │
│ ③ setValue(fieldName, value) - 값 설정 │
│ ↓ │
│ ④ 값 변경이 전체 폼 시스템에 전파 │
│ ↓ │
│ ⑤ 저장 버튼 클릭 시 beforeFormSave 이벤트 발생 │
│ ↓ │
│ ⑥ 모든 컴포넌트가 현재 값을 formData에 추가 │
│ ↓ │
│ ⑦ API 호출하여 저장 │
└─────────────────────────────────────────────────────────────┘
3. 컴포넌트 간 통신
┌─────────────────────────────────────────────────────────────┐
│ 컴포넌트 간 통신 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 버튼 컴포넌트 │ ──────→ │ 리피터 컴포넌트│ │
│ │ │ repeater│ │ │
│ │ v2-button │ Save │ v2-unified- │ │
│ │ -primary │ 이벤트 │ repeater │ │
│ └──────────────┘ └──────────────┘ │
│ │ │ │
│ │ masterRecordId │ │
│ └────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 검색 위젯 │ ──────→ │ 테이블 리스트 │ │
│ │ │ onFilter│ │ │
│ │ v2-table- │ Change │ v2-table- │ │
│ │ search-widget│ │ list │ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
컴포넌트 등록 구조
// frontend/lib/registry/components/index.ts
// V2 컴포넌트들 (화면관리 전용)
import "./v2-unified-repeater/UnifiedRepeaterRenderer";
import "./v2-button-primary/ButtonPrimaryRenderer";
import "./v2-split-panel-layout/SplitPanelLayoutRenderer";
import "./v2-aggregation-widget/AggregationWidgetRenderer";
import "./v2-card-display/CardDisplayRenderer";
import "./v2-numbering-rule/NumberingRuleRenderer";
import "./v2-table-list/TableListRenderer";
import "./v2-text-display/TextDisplayRenderer";
import "./v2-pivot-grid/PivotGridRenderer";
import "./v2-divider-line/DividerLineRenderer";
import "./v2-repeat-container/RepeatContainerRenderer";
import "./v2-section-card/SectionCardRenderer";
import "./v2-section-paper/SectionPaperRenderer";
import "./v2-rack-structure/RackStructureRenderer";
import "./v2-location-swap-selector/LocationSwapSelectorRenderer";
import "./v2-table-search-widget";
import "./v2-tabs-widget/tabs-component";
파일 구조 표준
각 V2 컴포넌트 폴더는 다음 구조를 따릅니다:
v2-{component-name}/
├── index.ts # 컴포넌트 Definition (V2 접두사)
├── types.ts # TypeScript 타입 정의
├── {Component}Component.tsx # 실제 컴포넌트 구현
├── {Component}ConfigPanel.tsx # 설정 패널
├── {Component}Renderer.tsx # 레지스트리 등록 및 래퍼
├── config.ts # 기본 설정값 (선택)
└── README.md # 사용 가이드 (선택)
개발 가이드라인
새 V2 컴포넌트 생성 시
v2-접두사로 폴더 생성- Definition 이름에
V2접두사 사용 (예:V2NewComponentDefinition) index.ts에서 import 추가- 엔티티 조인 지원 필수 구현
- 다국어 키 필드 추가 (
langKeyId,langKey)
체크리스트
- V2 폴더에서 작업 중인지 확인
- 원본 폴더는 수정하지 않음
- 컴포넌트 ID에
v2-접두사 사용 - Definition 이름에
V2접두사 사용 - 엔티티 조인 컬럼 지원
- 커스텀 테이블 설정 지원
- 다국어 필드 추가
관련 파일 목록
| 파일 | 역할 |
|---|---|
frontend/lib/api/entityJoin.ts |
엔티티 조인 API |
frontend/hooks/useFormCompatibility.ts |
폼 호환성 브릿지 |
frontend/components/unified/UnifiedFormContext.tsx |
통합 폼 Context |
frontend/lib/utils/multilangLabelExtractor.ts |
다국어 라벨 추출/매핑 |
frontend/contexts/ScreenMultiLangContext.tsx |
다국어 번역 Context |
frontend/lib/registry/components/index.ts |
컴포넌트 등록 |
참고 문서
- component-development-guide.mdc - 컴포넌트 개발 상세 가이드
- table-list-component-guide.mdc - 테이블 리스트 가이드