# INVYONE 아키텍처 결정 문서

> **작성일**: 2026-04-09
> **기반**: 2026-04-08~09 빌더 설계 세션
> **상태**: 확정 (Phase 1 기준)

---

## 1. 로우코드 플랫폼 정의

**"사용자는 필드를 배치하는 게 아니라, FieldConfig를 소비하는 컴포넌트를 배치한다"**

FieldConfig 단일 규격이 전체 시스템의 중심이다. 테이블 컬럼, 폼 입력, 검색 조건이 전부 같은 FieldConfig를 공유하며, 컴포넌트는 이 규격을 소비하여 각자의 방식으로 렌더링한다.

vex의 접근: 컴포넌트마다 별도 규격 (ColumnConfig 354줄, FilterConfig, FormField 등) → 변환 필요 → 에러 폭발
invyone의 접근: FieldConfig 하나 (~30줄) → 변환 불필요 → 컴포넌트 간 데이터 직통

---

## 2. 역할 모델

| 역할 | 타겟 | 레벨 | 하는 일 |
|---|---|---|---|
| **사용자** (업무담당자) | 중소기업 | L0~L1 | 완성된 화면 사용 + 간단 설정 (컬럼 순서, 필터 조건 등) |
| **개발자** (IT팀) | 중견 이상 | L1~L2 | 빌더에서 화면 구성 (컴포넌트 배치, 필드 설정, DataPort 연결) |

중소기업은 개발자 없이 사용자만으로 운영 가능해야 한다. 개발자 빌더를 먼저 만들고, 사용자 화면은 그 위에 올린다.

---

## 3. 컴포넌트 규격

### 3.1 FieldConfig (~30줄)

테이블/폼/검색 전부 같은 규격. 핵심 필드:

```
column(식별), label(표시), type(FieldType), visible, order, width, align,
required, editable, defaultValue, placeholder,
options(select용), ref(entity FK), format(포맷), computed(자동계산),
pk, system, searchable, sortable
```

FieldType 10종: `text | number | date | datetime | select | entity | checkbox | textarea | file | code`

### 3.2 ComponentType 8종 (팔레트)

| 컴포넌트 | 역할 | FieldConfig 사용 |
|---|---|---|
| **데이터 테이블** | 목록 표시, 행 선택 | fields를 컬럼으로 렌더 |
| **검색 필터** | 조건 입력, 테이블에 params 전달 | fields를 검색 입력으로 렌더 |
| **입력 폼** | 데이터 입력/수정 | fields를 폼 필드로 렌더 |
| **버튼** | 액션 실행 (ActionType 12종) | - |
| **통계 카드** | KPI/집계 표시 | fields의 aggregation |
| **차트** | 시각화 | fields 기반 데이터 |
| **텍스트/제목** | 라벨, 설명, 구분 텍스트 | - |
| **구분선** | 시각적 분리 | - |

### 3.3 팔레트 정리 (26개 → 8개)

제거된 것들과 이유:

| 제거 항목 | 이유 / 대체 |
|---|---|
| 레이아웃 (tabs, split-panel) | 프리셋이 처리. 내부 모델은 유지하되 팔레트에서 숨김 |
| 필드 | FieldConfig.type이 처리. 컴포넌트가 아님 |
| 개별 버튼 종류 | ActionType (버튼 컴포넌트의 속성) |
| button-bar | 버튼 컴포넌트로 통합 |
| pagination | 테이블에 내장 |
| 마스터-디테일 | 테이블+폼 조합 + DataPort 연결로 구현 |

### 3.4 DataPort 통신

컴포넌트 간 직접 참조 금지. 표준화된 포트로 통신:

```
[검색] --searchParams(params)--> [테이블] --selectedRow(row)--> [폼]
                                           --selectedRows(rows)--> [버튼(삭제)]
```

DataPortType: `row | rows | value | params`

output→input 매칭으로 연결. 데이터 형식이 동일하므로 변환 불필요.

### 3.5 Template = 3뷰 내장

```
Template {
  fields: FieldConfig[]     ← 유일한 진실의 원천, 3뷰가 공유
  views: {
    list: ViewConfig        ← 목록 화면 (컴포넌트 배열)
    create: ViewConfig      ← 등록 팝업
    edit: ViewConfig        ← 수정 팝업 (create 상속 가능)
  }
  connections: Connection[] ← DataPort 연결
}
```

vex는 목록/등록/수정을 3개 화면으로 따로 만들어야 했다. invyone은 하나의 Template에 3뷰가 내장되어 같은 fields를 공유한다.

---

## 4. 렌더링 계약 (스펙 2.2)

FieldType별로 테이블/폼/검색에서 다르게 렌더된다. 이 차이는 암묵적이면 안 되고 명시적이어야 한다:

| FieldType | 테이블 | 폼 | 검색 |
|---|---|---|---|
| `text` | 텍스트 그대로 | `<input type="text">` | `<input>` (부분 일치) |
| `number` | format 적용 (#,##0) | `<input type="number">` | `<input>` (범위: min~max) |
| `date` | format 적용 | DatePicker (단일) | **DateRangePicker (범위)** |
| `datetime` | format 적용 | DateTimePicker | DateTimeRangePicker |
| `select` | 텍스트 그대로 | `<select>` (단일) | **MultiSelect (다중)** |
| `entity` | ref.displayColumn 표시 | 검색 팝업 | 검색 팝업 (단일) |
| `checkbox` | check/cross 아이콘 | `<checkbox>` | `<select>` (전체/Y/N) |
| `textarea` | 말줄임 (...) | `<textarea>` | `<input>` (부분 일치) |
| `file` | 파일명 링크 | 파일 업로드 | -- (검색 불가) |
| `code` | 텍스트 그대로 | readonly | `<input>` (완전 일치) |

핵심: 검색에서 date→범위, select→다중으로 바뀌는 것은 SearchConfig의 렌더링 규칙이지 FieldType의 암묵적 속성이 아니다.

---

## 5. computed 실행 모델

**eval() / new Function() 절대 금지.**

```
허용: 'quantity * unit_price', 'SUM(order_items.amount)'
금지: 'fetch("/api")', 'console.log()', 'document.cookie'
```

구현: 스택 기반 수식 파서 (AST 또는 화이트리스트 토큰)
- 허용 토큰: 숫자, 필드명, +, -, *, /, (, ), SUM, COUNT, AVG, MIN, MAX
- 그 외 전부 거부
- 별도 유틸 (`lib/formula-parser.ts`)로 분리

---

## 6. required 검증

**0, false는 유효한 값이다. empty = null / undefined / '' 만.**

```typescript
function isFieldEmpty(value: any): boolean {
  return value === null || value === undefined || value === '';
}
```

JavaScript의 falsy 함정과 다른 비즈니스 규칙. 수량 0, 체크박스 false는 의미 있는 값.

---

## 7. 데이터 흐름 (전체 그림)

```
[table_type_columns DB]
        |  (자동 로드 / 수동 정의)
        v
[Template.fields: FieldConfig[]]  <-- 유일한 진실의 원천
        |  (공유)
   +---------+---------+
   v         v         v
 목록       등록       수정      <-- 3뷰가 같은 fields 공유
   |         |         |
   v         v         v
[Component[]]                    <-- 각 뷰의 컴포넌트 배열
   |
   v
[DataPort 연결]                  <-- 검색->테이블->폼 자동 흐름
   |
   v
[렌더러]                         <-- FieldConfig.type 보고 적절한 UI
```

---

## 8. vex와의 차이

| 항목 | vex | invyone |
|---|---|---|
| 필드 규격 | ColumnConfig(354줄) + FilterConfig + FormField = 전부 다름 | **FieldConfig 하나 (~30줄)** |
| 컴포넌트 간 통신 | 자체 구현, 컴포넌트마다 다름 | **DataPort 표준 프로토콜** |
| 컴포넌트 타입 | 33종 (v2-*), 각각 다른 규격 | **8종, 공통 base + 간결한 TypeConfig** |
| 테이블 설정 | TableListConfig 354줄 | **TableConfig ~15줄** |
| 화면 구조 | 목록/등록/수정 = 3개 화면 따로 만들어 연결 | **Template 1개에 3뷰 내장** |
| 필드 공유 | 화면마다 따로 정의 | **Template.fields를 모든 뷰가 공유** |
| 데이터 전달 | 컴포넌트별 커스텀 이벤트 | **output->input 자동 매칭** |

---

## 9. Phase 로드맵

### Phase 1: 레이아웃 + CRUD (현재)
- FieldConfig 정의, 빌더 UI, 프리셋 시스템
- 테이블/폼/검색 렌더러
- DataPort 기본 연결
- CRUD API 자동 생성

### Phase 2: 규칙 엔진
- 유효성 검증 (ValidationRule 배열)
- 필드 간 연동 (A 변경 시 B 자동 계산)
- 조건부 표시/숨김
- SearchConfig fieldOverrides

### Phase 3: 제어 플로우
- 노드 에디터 (수주 결재 완료 -> 발주 자동 생성 등)
- 회사별 자동화 체인
- test-vex의 node_flows 진화 버전

---

## 10. 기술 스택

| 영역 | 기술 |
|---|---|
| Frontend | Next.js (React) |
| Backend | Spring Boot (Java) |
| DB | PostgreSQL + JSONB |
| 빌더 | HTML mockup -> React 이식 예정 |
| 상태관리 | Zustand 또는 Context (미정) |
| 수식 파서 | 스택 기반 커스텀 파서 (lib/formula-parser.ts) |

---

## 참고 문서

| 문서 | 위치 |
|---|---|
| 컴포넌트 규격 v1.0 | `notes/gbpark/2026-04-08-invyone-component-spec.md` |
| 로우코드 플랫폼 SPEC | `notes/gbpark/2026-04-08-lowcode-platform-spec.md` |
| mockup 폴더 | `notes/gbpark/2026-04-08-invyone-mockup/` |
| 빌더 세션 로그 | `notes/gbpark/2026-04-09-builder-session-log.md` |
| 이전 세션 로그 | `notes/gbpark/2026-04-08-invyone-dev-session-log.md` |
