# INVYONE 로우코드 플랫폼 SPEC (v1.0)

> **상태**: DRAFT
> **작성일**: 2026-04-08
> **기반**: 2026-04-08 설계 토론 (로우코드 정의, 타겟 사용자, 역할 분리, 67개 화면 분석)
> **이전 문서**: `2026-04-08-invyone-rebuild-spec.md` (v0.2) — 기존 코드 위에 얹는 "리팩토링" 관점이었음. 본 문서는 **신규 설계** 관점.

---

## 0. 한 줄 요약

**테이블 메타데이터 기반으로 ERP 화면을 자동생성하고, 개발자 빌더에서 커스텀할 수 있는 로우코드 플랫폼.**

---

## 1. 로우코드란 무엇인가 (INVYONE 맥락)

### 1.1 정의

로우코드 = **코드를 쓰지 않고 비즈니스 화면을 만드는 것**.

하지만 레벨이 있다:

| 레벨 | 뭘 하는가 | 예시 |
|---|---|---|
| L0 노코드 | 설정만으로 완성 | Google Forms, Airtable |
| L1 로우코드 | 드래그앤드롭 + 간단한 규칙 | Retool, OutSystems |
| L2 프로코드 | 비주얼 빌더 + 코드 확장 | Mendix, Power Apps 고급 |
| L3 프레임워크 | 코드 기반, 보일러플레이트 자동화 | JHipster, Rails scaffold |

**INVYONE의 위치**: L0~L1 (중소기업) + L1~L2 (중견 이상)

### 1.2 현실 — 67개 화면이 말해주는 것

PM이 Cursor로 만든 화면 개발 HTML 67개를 분석한 결과:

```
67개 HTML 파일, 총 110,515줄, 평균 1,647줄/파일

구성 비율:
  JavaScript (비즈니스 로직)  67%  1,098줄
  CSS (스타일)               19%    314줄
  HTML (레이아웃)            14%    235줄

패턴:
  66/66 inline script (전부)
  55/66 CRUD 버튼
  54/66 검색 섹션
  52/66 모달/팝업
  51/66 테이블/그리드
  51/66 하드코딩 더미 데이터
```

**핵심 인사이트**: 레이아웃(14%)은 쉽게 템플릿화 가능. CSS(19%)는 공통화 가능. 진짜 문제는 **67%를 차지하는 JS 비즈니스 로직**. 하지만 이 로직도 분해하면 대부분이 공통 패턴(검색 필터링, 테이블 정렬, 모달 open/close, CRUD, 유효성검사)이다.

### 1.3 간편화의 범위

| 간편화 가능 (Phase 1~2) | 간편화 불가능 (코드 필요) |
|---|---|
| 테이블 → CRUD 화면 자동생성 | 회사별 고유 비즈니스 로직 |
| 필드 타입별 입력 컴포넌트 자동 매칭 | 복잡한 화면 간 데이터 흐름 |
| 검색/필터 자동 구성 | 외부 시스템 연동 |
| 기본 버튼(등록/수정/삭제) 자동 배치 | 고도로 커스텀된 UI |
| M-D 관계 자동 감지 | 리포트/차트 레이아웃 |
| 유효성검사 규칙 설정 | |
| 필드 간 연동 (A→B 자동 계산) | |
| 조건부 표시 (상태 따라 필드 숨기기) | |

---

## 2. 타겟 사용자

### 2.1 중소기업 — 업무 담당자

- **스킬**: 엑셀 정도
- **기대 레벨**: L0~L1 (설정만으로 화면 생성, 코드 없음)
- **사용 시나리오**: 테이블 선택 → 프리셋 선택 → 바로 사용. 필드 숨기기/라벨 수정 정도.
- **초기 세팅**: 우리(SI)가 해줌. 업무담당자는 사용자 역할로 씀.
- **커스텀 요청 시**: 우리가 원격으로 개발자 모드에서 수정.

### 2.2 중견 이상 — IT팀 (교육 후)

- **스킬**: 시스템 설정, SQL 기본
- **기대 레벨**: L1~L2 (빌더로 커스텀 + 규칙 설정)
- **사용 시나리오**: 레이아웃 변경, 블록 재배치, 제어 플로우 구성, 데이터 매핑.
- **초기 세팅**: 우리가 세팅 + IT팀 교육.
- **이후**: IT팀이 개발자 역할로 자체 커스텀.

---

## 3. 역할 모델

### 3.1 두 역할

| | 사용자 (User) | 개발자 (Developer) |
|---|---|---|
| 화면 사용 | O | O |
| 필드 숨기기/라벨/순서 변경 | O | O |
| 프리셋 선택 | O | O |
| 레이아웃 빌더 (블록 배치) | **X (안 보임)** | O |
| 제어 플로우 (노드 에디터) | **X** | O |
| 데이터 매핑 | **X** | O |
| 팝업 구조 편집 | **X** | O |
| 컴포넌트 속성 상세 설정 | **X** | O |

### 3.2 사용자 개인 설정 레이어

사용자가 변경한 것(필드 숨기기, 라벨, 순서)은 **개발자 템플릿 위에 얹히는 오버라이드 레이어**로 저장.

```
렌더링 우선순위:
  1. 개발자 템플릿 (기본값)
  2. 사용자 오버라이드 (있으면 덮어씌움)

개발자가 템플릿 재배포 → 사용자 오버라이드는 유지됨
  (단, 개발자가 필드를 삭제하면 해당 오버라이드는 자동 무시)
```

저장 구조:
```json
{
  "templateId": "tpl_order_mgmt",
  "userId": "user_123",
  "overrides": {
    "fields": {
      "fax_number": { "visible": false },
      "customer_name": { "label": "고객사" }
    },
    "fieldOrder": ["order_date", "customer_name", "amount", ...],
    "gridColumns": {
      "order_id": { "width": 80 },
      "customer_name": { "width": 200 }
    }
  }
}
```

---

## 4. 템플릿 구조 (핵심)

### 4.1 3축 분리: Data / View / Action

기존 test-vex는 screen_layouts_v3에 레이아웃+액션+데이터바인딩이 뒤섞여 있었다. INVYONE은 분리한다.

```
Template
├── data    — 어떤 데이터를 어떻게 가져오나
├── views   — 어떤 모양으로 보여주나
└── actions — 뭘 누르면 뭐가 일어나나
```

### 4.2 Template JSON 전체 구조

```json
{
  "templateId": "tpl_order_mgmt",
  "name": "수주관리",
  "version": 1,
  "category": "sales",
  "description": "수주 등록/조회/수정/삭제",

  "data": {
    "primary": {
      "table": "orders",
      "label": "수주",
      "fields": [
        {
          "column": "order_id",
          "label": "수주번호",
          "type": "code",
          "pk": true,
          "auto": true,
          "searchable": true,
          "gridVisible": true,
          "formVisible": false
        },
        {
          "column": "order_date",
          "label": "수주일",
          "type": "date",
          "required": true,
          "default": "today",
          "searchable": true,
          "gridVisible": true,
          "formVisible": true
        },
        {
          "column": "customer_id",
          "label": "거래처",
          "type": "entity",
          "entity": {
            "table": "customers",
            "valueColumn": "customer_id",
            "displayColumn": "customer_name",
            "searchColumns": ["customer_name", "biz_number"]
          },
          "required": true,
          "searchable": true,
          "gridVisible": true,
          "formVisible": true
        },
        {
          "column": "total_amount",
          "label": "합계금액",
          "type": "number",
          "format": "#,##0",
          "computed": "SUM(details.amount)",
          "gridVisible": true,
          "formVisible": true,
          "editable": false
        },
        {
          "column": "status",
          "label": "상태",
          "type": "select",
          "options": [
            { "value": "draft", "label": "임시저장" },
            { "value": "confirmed", "label": "확정" },
            { "value": "completed", "label": "완료" },
            { "value": "cancelled", "label": "취소" }
          ],
          "default": "draft",
          "searchable": true,
          "gridVisible": true,
          "formVisible": true
        }
      ]
    },
    "details": [
      {
        "table": "order_items",
        "label": "수주상세",
        "fk": "order_id",
        "fields": [
          {
            "column": "item_id",
            "label": "품목",
            "type": "entity",
            "entity": {
              "table": "items",
              "valueColumn": "item_id",
              "displayColumn": "item_name"
            },
            "required": true
          },
          {
            "column": "quantity",
            "label": "수량",
            "type": "number",
            "required": true
          },
          {
            "column": "unit_price",
            "label": "단가",
            "type": "number",
            "format": "#,##0"
          },
          {
            "column": "amount",
            "label": "금액",
            "type": "number",
            "format": "#,##0",
            "computed": "quantity * unit_price",
            "editable": false
          }
        ]
      }
    ]
  },

  "views": {
    "list": {
      "layout": "basic",
      "toolbar": {
        "title": "수주관리",
        "buttons": ["create", "delete", "export", "print"]
      },
      "search": {
        "fields": "auto"
      },
      "grid": {
        "columns": "auto",
        "defaultSort": { "column": "order_date", "direction": "desc" },
        "pageSize": 20,
        "rowSelection": "multiple"
      }
    },
    "create": {
      "layout": "master-detail",
      "title": "수주 등록",
      "form": {
        "sections": [
          {
            "label": "기본정보",
            "columns": 2,
            "fields": ["order_date", "customer_id", "status"]
          },
          {
            "label": "비고",
            "columns": 1,
            "fields": ["remarks"]
          }
        ]
      },
      "detailGrid": {
        "table": "order_items",
        "editable": true,
        "addRow": true,
        "deleteRow": true
      },
      "buttons": ["save", "cancel"]
    },
    "edit": {
      "extends": "create",
      "title": "수주 수정"
    }
  },

  "actions": {
    "builtin": {
      "create": { "label": "등록", "icon": "plus" },
      "save": { "label": "저장" },
      "delete": { "label": "삭제", "confirm": true },
      "export": { "label": "엑셀", "format": "xlsx" },
      "print": { "label": "인쇄" }
    },
    "custom": []
  },

  "rules": {
    "validation": [
      {
        "field": "order_date",
        "rule": "required",
        "message": "수주일은 필수입니다"
      },
      {
        "field": "customer_id",
        "rule": "required",
        "message": "거래처를 선택하세요"
      },
      {
        "condition": "details.length == 0",
        "message": "수주상세를 1건 이상 입력하세요"
      }
    ],
    "computation": [
      {
        "target": "order_items.amount",
        "formula": "quantity * unit_price"
      },
      {
        "target": "total_amount",
        "formula": "SUM(order_items.amount)"
      }
    ],
    "visibility": [],
    "flows": []
  }
}
```

### 4.3 핵심 설계 결정

#### "auto" 키워드

`search.fields: "auto"`는 **필드 정의에서 `searchable: true`인 것을 자동으로 검색바에 넣겠다**는 뜻.
`grid.columns: "auto"`는 **필드 정의에서 `gridVisible: true`인 것을 자동으로 그리드 컬럼으로 쓰겠다**는 뜻.

자동생성(경로 A)에서는 전부 "auto"로 시작. 개발자가 수동으로 바꾸고 싶으면 명시적 배열로 교체.

```json
// 경로 A: 자동생성 (기본)
"search": { "fields": "auto" }

// 경로 B: 수동구성 (개발자가 직접 지정)
"search": {
  "fields": [
    { "column": "order_date", "type": "daterange" },
    { "column": "customer_id" },
    { "column": "status", "type": "multiselect" }
  ]
}
```

#### 한 덩어리 원칙

템플릿 = **메인화면(list) + 등록팝업(create) + 수정팝업(edit)이 한 JSON 안에**.

test-vex는 모달을 별도 화면으로 만들고 버튼으로 연결했다 (직렬적, 7단계). INVYONE은 한 템플릿이 자기 팝업을 내장한다.

#### edit extends create

수정 화면은 대부분 등록 화면과 같고, 제목/일부 필드 readonly만 다르다.
`"extends": "create"`로 중복 제거. 차이점만 오버라이드.

---

## 5. 컴포넌트 시스템

### 5.1 화면 레벨 컴포넌트 (5종)

| 컴포넌트 | 역할 | 비고 |
|---|---|---|
| `Toolbar` | 제목 + 액션 버튼 (등록/삭제/엑셀/인쇄) | 모든 화면 공통 |
| `SearchPanel` | 검색 조건 (필드 자동 매핑) | searchable 필드 기반 |
| `DataGrid` | 목록 (정렬/페이징/선택/인라인편집) | gridVisible 필드 기반 |
| `FormPanel` | 입력 폼 (등록/수정) | formVisible 필드 기반 |
| `TabGroup` | 탭 분할 (M-D, 멀티뷰) | detail 테이블이 2개+ 일 때 |

### 5.2 필드 레벨 컴포넌트 (10종)

| type | 렌더링 | 매핑 |
|---|---|---|
| `text` | 텍스트 입력 | 일반 문자열 |
| `number` | 숫자 입력 + 포맷팅 | 금액, 수량 등 |
| `date` | 날짜 픽커 | 날짜 |
| `datetime` | 날짜+시간 픽커 | 일시 |
| `select` | 드롭다운 | options 배열 기반 |
| `entity` | 검색 팝업 + 표시값 | FK 참조 (거래처, 품목 등) |
| `checkbox` | 체크박스 | boolean |
| `textarea` | 장문 입력 | 비고, 메모 |
| `file` | 파일 첨부 | 첨부파일 |
| `code` | 자동채번 (readonly) | PK, 문서번호 |

### 5.3 test-vex 15종과의 매핑

```
test-vex input_type     →  INVYONE type
─────────────────────────────────────────
text (30,136건)         →  text
date (4,722건)          →  date
number (1,350건)        →  number
category (987건)        →  select (options를 category 값에서 로드)
entity (816건)          →  entity
numbering (244건)       →  code
code (192건)            →  code
select (160건)          →  select
textarea (111건)        →  textarea
image (75건)            →  file (이미지 프리뷰 포함)
checkbox (60건)         →  checkbox
file (21건)             →  file
radio (13건)            →  select (3개 이하면 radio 렌더링)
datetime (2건)          →  datetime
boolean (1건)           →  checkbox
```

test-vex의 15종 → INVYONE 10종으로 정리 (유사 타입 통합).

---

## 6. 레이아웃 프리셋

### 6.1 메인 화면 프리셋 (3종)

```
[기본형 basic]
┌─────────────────────────────────────┐
│ Toolbar                             │
├─────────────────────────────────────┤
│ SearchPanel                         │
├─────────────────────────────────────┤
│ DataGrid                            │
│                                     │
│                                     │
└─────────────────────────────────────┘
→ 가장 많이 쓰는 패턴. 단일 테이블 CRUD.

[분할형 split]
┌─────────────────────────────────────┐
│ Toolbar                             │
├─────────────────────────────────────┤
│ SearchPanel                         │
├──────────────────┬──────────────────┤
│ DataGrid (목록)  │ DetailPanel      │
│                  │ (선택 행 상세)    │
│                  │                  │
└──────────────────┴──────────────────┘
→ 목록 클릭 → 우측에 상세. 거래처, 품목정보 등.

[탭형 tab]
┌─────────────────────────────────────┐
│ Toolbar                             │
├─────────────────────────────────────┤
│ SearchPanel                         │
├─────────────────────────────────────┤
│ [탭A] [탭B] [탭C]                   │
│ ┌─────────────────────────────────┐ │
│ │ DataGrid (탭별 다른 데이터)     │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────┘
→ 같은 화면에서 여러 뷰. 수주현황(월별/거래처별/품목별) 등.
```

### 6.2 팝업(등록/수정) 프리셋 (2종)

```
[기본 폼 single]
┌─────────────────────────────────────┐
│ 제목                          [X]   │
├─────────────────────────────────────┤
│ ┌─ 기본정보 ──────────────────────┐ │
│ │ 필드1: [    ]  필드2: [    ]    │ │
│ │ 필드3: [    ]  필드4: [    ]    │ │
│ └─────────────────────────────────┘ │
│ ┌─ 비고 ──────────────────────────┐ │
│ │ [                              ]│ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────┤
│                    [저장]  [취소]    │
└─────────────────────────────────────┘
→ 단순 폼. 필드 수 적을 때.

[마스터-디테일 master-detail]
┌─────────────────────────────────────┐
│ 제목                          [X]   │
├─────────────────────────────────────┤
│ ┌─ 기본정보 (마스터) ─────────────┐ │
│ │ 필드1: [    ]  필드2: [    ]    │ │
│ └─────────────────────────────────┘ │
│ ┌─ 상세 (디테일) ─────────────────┐ │
│ │ [+ 행 추가]                     │ │
│ │ ┌───┬────┬────┬────┬────┐      │ │
│ │ │ # │품목│수량│단가│금액│      │ │
│ │ ├───┼────┼────┼────┼────┤      │ │
│ │ │ 1 │... │... │... │... │      │ │
│ │ └───┴────┴────┴────┴────┘      │ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────┤
│          합계: 1,234,567            │
│                    [저장]  [취소]    │
└─────────────────────────────────────┘
→ 수주, 발주, 견적 등 헤더+상세 구조.
```

---

## 7. 자동생성 (경로 A) vs 수동구성 (경로 B)

### 7.1 경로 A: 자동생성

```
개발자가 하는 것:
  1단계: 테이블 선택 (드롭다운)
  2단계: 프리셋 선택 (basic / split / tab)
  3단계: [생성] 클릭
  → 끝. 메타데이터 기반으로 Template JSON 자동 생성.

자동으로 결정되는 것:
  - fields: table_type_columns에서 로드 (타입, 라벨, 순서, 표시여부)
  - search: searchable 필드 자동 추가
  - grid: gridVisible 필드 자동 추가
  - form: formVisible 필드 자동 배치
  - detail: table_relationships에서 M-D 자동 감지
  - options: table_column_category_values에서 자동 로드
  - entity: table_relationships에서 FK 자동 감지

이후 원하면:
  - 속성 패널에서 필드 on/off, 라벨 수정, 순서 변경
  - 프리셋 변경
  - 섹션 추가/제거
```

### 7.2 경로 B: 수동구성

```
개발자가 하는 것:
  1단계: 빈 템플릿 생성
  2단계: 테이블 연결 (하나 이상)
  3단계: 레이아웃 블록을 캔버스에 직접 배치
  4단계: 각 블록에 필드를 수동 할당
  5단계: 액션/규칙 설정

언제 쓰는가:
  - 여러 테이블을 비표준으로 조합할 때
  - 프리셋으로 안 되는 특수 레이아웃
  - SI 프로젝트에서 고객 요구사항이 표준과 다를 때
```

### 7.3 두 경로의 관계

경로 A로 시작 → 필요하면 경로 B로 전환 가능.
경로 A가 생성한 Template JSON을 경로 B의 빌더에서 열면 그대로 편집 가능.
**경로 A는 경로 B의 shortcut**. 같은 결과물(Template JSON)을 만드는 두 입구.

---

## 8. DB 설계 (신규)

### 8.1 원칙

- 기존 test-vex DB 방식에 구속받지 않음 (신규 설계)
- Template JSON은 **JSONB 컬럼 하나**에 통째로 저장 (정규화 X)
  - 이유: 템플릿 구조가 자주 변하고, 부분 쿼리할 일이 거의 없음
  - 렌더러는 JSON을 통째로 읽어서 그림
- 메타데이터(테이블/필드 정보)는 정규화 유지

### 8.2 테이블

```sql
-- 템플릿 정의 (개발자가 빌더에서 만든 것)
CREATE TABLE templates (
  template_id     VARCHAR(50) PRIMARY KEY,
  company_code    VARCHAR(20) NOT NULL,
  name            VARCHAR(100) NOT NULL,
  category        VARCHAR(30),           -- sales, production, purchase, ...
  description     TEXT,
  template_json   JSONB NOT NULL,        -- 4.2의 전체 구조
  version         INTEGER DEFAULT 1,
  status          VARCHAR(10) DEFAULT 'draft',  -- draft / published
  created_by      VARCHAR(50),
  created_at      TIMESTAMP DEFAULT NOW(),
  updated_at      TIMESTAMP DEFAULT NOW()
);

-- 사용자 오버라이드 (사용자가 개인 설정한 것)
CREATE TABLE user_template_overrides (
  id              SERIAL PRIMARY KEY,
  template_id     VARCHAR(50) REFERENCES templates(template_id),
  user_id         VARCHAR(50) NOT NULL,
  company_code    VARCHAR(20) NOT NULL,
  overrides_json  JSONB NOT NULL,        -- 3.2의 오버라이드 구조
  updated_at      TIMESTAMP DEFAULT NOW(),
  UNIQUE(template_id, user_id, company_code)
);

-- 대시보드 (사용자의 작업 공간)
CREATE TABLE dashboards (
  dashboard_id    VARCHAR(50) PRIMARY KEY,
  company_code    VARCHAR(20) NOT NULL,
  owner_user_id   VARCHAR(50),
  name            VARCHAR(100) NOT NULL,
  is_default      BOOLEAN DEFAULT FALSE,
  elements_json   JSONB,                 -- 배치된 템플릿 카드 목록
  created_at      TIMESTAMP DEFAULT NOW(),
  updated_at      TIMESTAMP DEFAULT NOW()
);

-- 테이블 메타데이터 (자동생성의 기반)
-- 기존 table_type_columns 개념이지만 정리된 버전
CREATE TABLE table_metadata (
  id              SERIAL PRIMARY KEY,
  company_code    VARCHAR(20) NOT NULL,
  table_name      VARCHAR(100) NOT NULL,
  table_label     VARCHAR(100),          -- 한글명
  column_name     VARCHAR(100) NOT NULL,
  column_label    VARCHAR(100),          -- 한글 라벨
  input_type      VARCHAR(30) NOT NULL,  -- text/number/date/select/entity/...
  display_order   INTEGER DEFAULT 0,
  is_required     BOOLEAN DEFAULT FALSE,
  is_searchable   BOOLEAN DEFAULT FALSE,
  is_grid_visible BOOLEAN DEFAULT TRUE,
  is_form_visible BOOLEAN DEFAULT TRUE,
  default_value   VARCHAR(200),
  options_json    JSONB,                 -- select 타입의 선택지
  entity_ref_json JSONB,                 -- entity 타입의 참조 정보
  format          VARCHAR(50),           -- number 포맷 등
  UNIQUE(company_code, table_name, column_name)
);

-- 테이블 관계 (M-D 자동 감지용)
CREATE TABLE table_relations (
  id              SERIAL PRIMARY KEY,
  company_code    VARCHAR(20) NOT NULL,
  parent_table    VARCHAR(100) NOT NULL,
  child_table     VARCHAR(100) NOT NULL,
  fk_column       VARCHAR(100) NOT NULL,
  relation_type   VARCHAR(20) DEFAULT 'detail',  -- detail / reference
  UNIQUE(company_code, parent_table, child_table)
);

-- 제어 플로우 (Phase 3, 구조만 잡아둠)
CREATE TABLE control_flows (
  flow_id         VARCHAR(50) PRIMARY KEY,
  company_code    VARCHAR(20) NOT NULL,
  template_id     VARCHAR(50) REFERENCES templates(template_id),
  name            VARCHAR(100),
  trigger_action  VARCHAR(30),           -- save / approve / delete / ...
  nodes_json      JSONB,                 -- 노드 에디터의 노드+엣지
  status          VARCHAR(10) DEFAULT 'active',
  created_at      TIMESTAMP DEFAULT NOW()
);
```

### 8.3 기존 test-vex 테이블과의 관계

| test-vex | INVYONE | 비고 |
|---|---|---|
| screen_definitions | templates | 통합 (화면 정의 + 레이아웃 한 덩어리) |
| screen_layouts_v3 | templates.template_json | JSON으로 흡수 |
| table_type_columns | table_metadata | 정리된 버전 |
| table_column_category_values | table_metadata.options_json | JSONB로 흡수 |
| table_labels | table_metadata.table_label | 같은 테이블에 통합 |
| table_relationships | table_relations | 거의 동일 |
| node_flows | control_flows | 구조 동일, Phase 3 |
| button_action_standards | templates.template_json.actions | JSON에 내장 |

---

## 9. 개발자 빌더 UI

### 9.1 구조

```
┌─────────────────────────────────────────────────────────┐
│ [탑바] 템플릿명  |  [저장] [미리보기] [게시]              │
├────────┬────────────────────────────────┬───────────────┤
│ 좌측   │ 중앙                           │ 우측          │
│ 패널   │ 캔버스 (미리보기)              │ 속성 패널     │
│        │                                │               │
│ 테이블 │  ┌─ Toolbar ────────────────┐  │ [선택된 블록  │
│ 선택   │  │ 수주관리  [등록][삭제]   │  │  의 속성]     │
│        │  ├──────────────────────────┤  │               │
│ 프리셋 │  │ SearchPanel              │  │ 라벨: [    ]  │
│ 선택   │  ├──────────────────────────┤  │ 타입: [v]     │
│        │  │ DataGrid                 │  │ 필수: [x]     │
│ 블록   │  │ ┌──┬──┬──┬──┐          │  │ 검색: [x]     │
│ 팔레트 │  │ │  │  │  │  │          │  │ ...           │
│        │  │ └──┴──┴──┴──┘          │  │               │
│ 필드   │  └──────────────────────────┘  │ [필드 목록]   │
│ 목록   │                                │ ☑ 수주번호    │
│        │                                │ ☑ 수주일      │
│        │                                │ ☑ 거래처      │
│        │                                │ ☐ FAX        │
└────────┴────────────────────────────────┴───────────────┘
```

### 9.2 워크플로우

```
[경로 A: 자동생성]
1. 좌측 패널에서 테이블 선택 (orders)
2. 프리셋 선택 (basic)
3. [⚡ 자동 생성] 클릭
4. 중앙 캔버스에 화면 미리보기 표시
5. 필요하면 우측 속성 패널에서 조정
6. [게시] → 사용자에게 노출

[경로 B: 수동구성]
1. 빈 캔버스
2. 좌측 팔레트에서 블록 드래그 → 캔버스에 배치
3. 각 블록에 테이블/필드 연결
4. 우측 속성 패널에서 상세 설정
5. [게시]
```

### 9.3 디자인 원칙

- **IDE 스타일** — 코스믹 디자인 X. 중성 다크 그레이, 글로우 없음.
- 다크/라이트 둘 다 가독성 확보.
- 코스믹 디자인(v5)은 **사용자 화면에만** 적용.
- 개발자 빌더는 **도구**. 화려하면 안 됨.

---

## 10. 사용자 화면 (템플릿 렌더러)

### 10.1 역할

Template JSON을 받아서 실제 동작하는 화면을 그리는 것.

```
Template JSON (DB에서 로드)
  + User Override JSON (있으면)
  = 최종 렌더링 스펙
  → 렌더러가 React 컴포넌트로 그림
```

### 10.2 렌더링 순서

```
1. template_json 로드
2. user_template_overrides 로드 (있으면)
3. 머지: template 기본값 + user override
4. data.primary.fields → 필드 정의 확정
5. views.list.layout → 레이아웃 결정
6. 화면 렌더링:
   a. Toolbar (title + buttons)
   b. SearchPanel (searchable 필드)
   c. DataGrid (gridVisible 필드)
7. 사용자가 [등록] 클릭 → views.create 기반으로 팝업 렌더링
8. 사용자가 행 클릭 → views.edit 기반으로 팝업 렌더링
9. 각 액션 → actions.builtin/custom 정의에 따라 실행
```

### 10.3 디자인

- v5 Cosmic Glassmorphism 적용 (사용자 화면)
- `v5-layout.css` 토큰 사용
- 다크/라이트 모드 대응

---

## 11. Phase별 로드맵

### Phase 1: 레이아웃 + CRUD 자동화

**목표**: 테이블 선택 → 화면 자동생성 → 기본 CRUD 동작

- 개발자 빌더: 자동생성(경로 A) + 속성 패널
- 템플릿 렌더러: list/create/edit 기본 렌더링
- 컴포넌트: 화면 5종 + 필드 10종
- 프리셋: 메인 3종 + 팝업 2종
- DB: templates, table_metadata, table_relations
- API: 범용 CRUD (template 기반 자동 쿼리 생성)

**완료 조건**: 수주관리 화면을 빌더에서 3클릭으로 만들고, 실제 데이터로 CRUD 동작.

### Phase 2: 규칙 엔진

**목표**: 코드 없이 비즈니스 규칙 설정

- 유효성검사 규칙 빌더 (필수, 범위, 중복, 커스텀 조건)
- 필드 간 연동 (A 변경 → B 자동 계산)
- 조건부 표시 (상태에 따라 필드/섹션 숨기기)
- 행 수준 권한 (내 부서만 보기 등)

### Phase 3: 제어 플로우 (고급 자동화)

**목표**: 화면 간 자동화 체인

- 노드 에디터로 제어 플로우 구성
- 트리거: 저장/결재/삭제 후
- 액션: 다른 테이블에 자동 등록, 상태 변경, 알림
- 예: "수주 결재 완료 → 발주 자동 생성"

### Phase 4: 확장

- 수동구성(경로 B) 빌더 고도화
- 리포트/차트 컴포넌트
- 모바일 대응
- 외부 시스템 연동 노드
- 커스텀 스크립트 (IT팀용, 코드 에디터 내장)

---

## 12. 기술 스택

| 레이어 | 기술 | 비고 |
|---|---|---|
| Frontend | Next.js (React) | 기존 invyone |
| 빌더 UI | React + 자체 드래그앤드롭 | IDE 스타일 |
| 사용자 화면 | React + v5 CSS | Cosmic 디자인 |
| Backend | Spring Boot (Java) | 기존 invyone |
| DB | PostgreSQL + JSONB | 템플릿은 JSONB |
| API | REST | 범용 CRUD + 템플릿 관리 |

---

## 13. test-vex와의 차이 요약

| | test-vex (기존) | INVYONE (신규) |
|---|---|---|
| 화면 만들기 | 7단계 수동 설정 | 2단계 (테이블 선택 → 프리셋) |
| 화면 정의 | 5개 테이블에 흩어짐 | Template JSON 한 덩어리 |
| 팝업 | 별도 화면 + 버튼 연결 | 템플릿에 내장 (한 덩어리) |
| 컴포넌트 | 70종 | 15종 (화면 5 + 필드 10) |
| 디자인 | 화면마다 제각각 | 공통 디자인 시스템 |
| 사용자 커스텀 | 없음 | 오버라이드 레이어 |
| 역할 분리 | 없음 (전부 개발자) | 사용자 / 개발자 |
| 비즈니스 로직 | 코드 | Phase별로 로우코드 확대 |

---

## 14. 다음 단계

1. 이 스펙을 기반으로 **Template JSON 스키마 확정** (TypeScript 타입 정의)
2. **개발자 빌더 mockup 재작업** (기존 08-admin-builder.js 구조 재정리)
3. **범용 CRUD API 설계** (template JSON을 읽어서 자동으로 SQL 생성하는 방식)
4. Phase 1 구현 시작
