# 회사관리 UI 스키마 & 데이터 소스 레퍼런스

작성일: 2026-04-24
작성자: gbpark
대상 UI: `admin-companyList-v9-systemized.jsx` (v9 accordion 메인 화면)
관련 마이그레이션: `db/migrations/RUN_081_MIGRATION.md`
관련 실행계획: `notes/gbpark/2026-04-24-company-db-provisioning-execution-plan.md`

---

## 요약

목업 v9 accordion row 가 요구하는 **16개 필드** 중:
- **8개**는 `COMPANY_MNG` 정적 컬럼 (기존 + 081 에서 추가)
- **4개**는 런타임 집계 (`CompanyStatsService` 가 계산)
- **2개**는 MVP placeholder (추후 데이터 파이프라인 생기면 채움)
- **2개**는 화면에서 derive (표시 로직)

신규 API: `GET /api/admin/provisioning/companies-stats`

---

## 1. 목업 ↔ 데이터 소스 매핑

### 1.1 정적 필드 (COMPANY_MNG 컬럼)

| 목업 필드 | COMPANY_MNG 컬럼 | 비고 |
|---|---|---|
| `code` | `COMPANY_CODE` | 기존 |
| `name` | `COMPANY_NAME` | 기존 |
| `sub` | `SUBDOMAIN` | 079 추가 |
| `brn` | `BUSINESS_REGISTRATION_NUMBER` | 기존 |
| `owner` | `REPRESENTATIVE_NAME` | 기존. 목업이 `owner` 이지만 그대로 매핑 |
| `created` | `CREATED_DATE` | 기존 |
| `writer` | `WRITER` | 기존 |
| **`plan`** | `PLAN` | ★ 081 추가. 기본값 `'Starter'` |
| **`industry`** | `INDUSTRY` | ★ 081 추가. 기본 NULL |
| **`templates`** | `TEMPLATES_COUNT` | ★ 081 추가. 프로비저닝 시 `CompanyProvisioningService.initiate()` 가 "필수(3) + 선택 n" 합산해서 저장 |

추가로 DB 라우팅 관련 (079):
| 목업에 없지만 UI 에서 파생 | 소스 |
|---|---|
| `{prefix}.invyone.com` 프리뷰 | `SUBDOMAIN` + 하드코딩 도메인 |
| `{prefix}_invyone` DB 명 | `DB_NAME` |
| 상태 dot | `DB_STATUS` |

### 1.2 런타임 집계 (CompanyStatsService)

| 목업 필드 | 계산 방법 | 구현 파일:함수 |
|---|---|---|
| `users` | tenant DB `SELECT count(*) FROM user_info` | `CompanyStatsService.enrichOne()` |
| `db_size_bytes` (숨김) | 메타 서버 `SELECT pg_database_size(?)` | 같음 |
| `db_size` ("4.2 GB" 포맷) | `formatBytes(bytes)` | 같음 |
| `db_pct` (0~100) | `bytes / (DB_QUOTA_GB * 1GB) * 100` | 같음 |

`DB_QUOTA_GB` 는 081 에서 추가된 컬럼. 회사별 할당량. 기본 20 GB. 추후 플랜별 자동 결정 로직 추가 가능.

### 1.3 MVP Placeholder (나중에 구현)

| 목업 필드 | 현재 값 | 언제 채움 |
|---|---|---|
| `active30` | `0` | `user_info` 또는 별도 `login_history` 테이블에 `last_login_date` 컬럼 생기면 ` WHERE last_login_date > NOW() - INTERVAL '30 days'` 로 구현 |
| `spark` | `[]` (빈 배열) | 일자별 사용자 집계 테이블 (`daily_active_users` 같은) 생기면 최근 12일 데이터 반환 |

### 1.4 화면에서 derive (백엔드 추가 작업 없음)

| 목업 필드 | 계산 |
|---|---|
| `url_preview` (`qnc.invyone.com`) | `${subdomain}.invyone.com` |
| `db_name_display` (`qnc_invyone`) | `db_name` (이미 있음) |

---

## 2. 신규 API

### `GET /api/admin/provisioning/companies-stats`

회사관리 v9 accordion 메인 화면 전용.

**권한**: SUPER_ADMIN (개발 모드 익명 허용)

**응답 예시**:
```json
[
  {
    "company_code": "COMPANY_10",
    "company_name": "큐엔씨",
    "subdomain": "qnc",
    "db_name": "qnc_invyone",
    "db_host": "183.99.177.40",
    "db_status": "active",
    "status": "active",
    "plan": "Starter",
    "industry": null,
    "owner": "정대표",
    "brn": "456-78-90123",
    "email": "admin@qnc.kr",
    "templates": 3,
    "db_quota_gb": 20,
    "created": "2026-04-24T...",
    "writer": "SUPER_ADMIN",

    "users": 31,
    "active30": 0,
    "db_size": "820 MB",
    "db_size_bytes": 859963392,
    "db_pct": 4,
    "spark": []
  },
  ...
]
```

정렬: `CREATED_DATE DESC`.

**N+1 주의**: 회사 N 개 → tenant DB N 번 + 메타 서버 N 번 접속. 현재 MVP 직렬 실행. 회사 50 개 넘어가면 병렬(`CompletableFuture`) 또는 5분 캐시 도입.

---

## 3. 081 마이그레이션 요약

```sql
ALTER TABLE COMPANY_MNG
  ADD COLUMN IF NOT EXISTS PLAN             VARCHAR(20) DEFAULT 'Starter',
  ADD COLUMN IF NOT EXISTS INDUSTRY         VARCHAR(50),
  ADD COLUMN IF NOT EXISTS TEMPLATES_COUNT  INT         DEFAULT 0,
  ADD COLUMN IF NOT EXISTS DB_QUOTA_GB      INT         DEFAULT 20;
```

- `PLAN` 기본 'Starter' → 기존 회사들도 자동 'Starter'.
- `TEMPLATES_COUNT` 는 신규 프로비저닝부터만 채워짐. 기존 회사들은 0.
- `INDUSTRY` NULL → UI 에서 "—" 로 표시.

---

## 4. 관련 코드 변경

### 신규
- `com/erp/provisioning/CompanyStatsService.java` — derived 집계
- `mapper/provisioning.xml` → `listCompaniesForUi` select 추가

### 수정
- `com/erp/provisioning/CompanyProvisioningService.java` → `initiate()` 에 templates_count 계산
- `com/erp/provisioning/ProvisioningController.java` → `/companies-stats` 엔드포인트 + `CompanyStatsService` 주입
- `mapper/provisioning.xml` → `insertCompanyWithTenant` 에 `PLAN`, `INDUSTRY`, `TEMPLATES_COUNT` 컬럼 추가

---

## 5. Phase 3-B 프론트가 할 일 정리

1. `GET /api/admin/provisioning/companies-stats` 호출해 회사 목록 획득
2. 응답 스키마 그대로 `R9` mock 자리에 주입
3. 필요 시 AccRow 가 `sub` 변수 대신 `subdomain` 사용하도록 네이밍 일관화

### 미구현 필드 표시 정책

- `active30 = 0` 이면 표시하되 "—" 로 회색 처리 추천
- `spark.length === 0` 이면 기존 Spark 컴포넌트가 "—" 반환 (이미 목업에 로직 있음)
- `industry === null` 이면 "—" 표시

---

## 6. 추후 작업 항목

| 항목 | 우선순위 | 트리거 |
|---|---|---|
| `active30` 구현 | 중 | `user_info.last_login_date` 컬럼 또는 `login_history` 테이블 생성 |
| `spark` 일자별 집계 | 낮 | 일 단위 배치로 `daily_active_users` 테이블 생성 |
| `companies-stats` 병렬 집계 | 중 | 회사 수 50 개 넘으면 |
| `industry` 선택 드롭다운 | 낮 | 마법사 Step1 에 "업종" 필드 추가 시 |
| `plan` 선택 UI | 낮 | 과금 시스템 도입 시점 |
| `DB_QUOTA_GB` 플랜 연동 | 낮 | 플랜별 자동 할당 (Starter=5, Standard=20, Enterprise=100) |

---

이 문서는 나중에 위 필드들을 실제 데이터로 채울 때 **체크리스트**로 쓰면 됨. 컬럼은 다 깔려있으니 추가 마이그레이션 없이 값만 채우면 화면이 바로 살아남.
