# 템플릿 → 대시보드 연결 작업 진행 현황 (2026-04-19)

스튜디오(ScreenDesigner)에서 만든 템플릿을 대시보드(= 사용자 메뉴) 카드로 배치하는 플로우 구축.

## 설계 결정

- **옵션 A 채택**: 기존 `screens` 테이블은 VEX 레거시 그대로 두고, 신규 `templates` 테이블을 사용
- **대시보드 = 사용자 메뉴**: 대시보드 생성 시 `MENU_INFO` 에 자동 등록 (MENU_DESC='dashboard:{id}' 태그로 구분)
- **단일 대시보드 모드**: 한 대시보드에 카드 여러 장, 각 카드 = 템플릿 1개

## 수정/신규 파일

### 프론트엔드

| 파일 | 상태 | 역할 |
|---|---|---|
| `lib/utils/templateAdapter.ts` | 신규 | 스튜디오 layout ↔ `templates.jsonb` (fields/views/connections) 변환. `saveTemplate`, `loadTemplateAsLayout`, `createTemplate` |
| `components/screen/ScreenDesigner.tsx` | 수정 (4군데 분기 추가) | `selectedScreen.template_id` 가 있으면 screenApi 대신 templateAdapter 경로로 저장/로드 |
| `app/(main)/admin/builder/page.tsx` | 재작성 | screenApi → `getTemplateList` / `insertTemplate`. `templateToScreenDef` 변환 추가 |
| `components/dash/TemplateRenderer.tsx` | 신규 | 대시보드 카드 안에서 템플릿을 렌더. V2 포맷(url + overrides) 파싱 → ComponentRegistry 조회 → 컴포넌트 렌더. absolute 좌표 + @container 기반 반응형 |
| `lib/registry/components/stats/StatsComponent.tsx` | 수정 | `asStyleObject` — indexed-object(예: `{0:"c",1:"a"}`) 방어 |
| `components/screen/RealtimePreviewDynamic.tsx` | 수정 | `sanitizeStyleKeys` — 숫자 키 style 잔재 제거 (StatsComponent 런타임 에러 근본 원인이 이 파일이었음) |
| 기타 21개 컴포넌트 | 수정 | `filterDOMProps` 적용해 DOM prop warning 제거 |

### 백엔드

| 파일 | 상태 | 역할 |
|---|---|---|
| `backend-spring/.../service/DashboardService.java` | 수정 | `insertDashboard` / `updateDashboard` / `deleteDashboard` 호출 시 `MENU_INFO` 행 자동 동기화 |
| `backend-spring/.../mapper/admin.xml` | 수정 | `selectMenuByDesc`, `updateMenuByDesc`, `deleteMenuByDesc` 쿼리 추가 |

## 해결한 이슈

- **Registry 미초기화**: `(unknown) 미등록 컴포넌트` → `import '@/lib/registry/components'` side-effect import 추가
- **V2 포맷 인식 실패**: DB 가 `componentId` 대신 `url: "@/lib/registry/components/table"` + `overrides: {...}` 로 저장 → `extractIdFromUrl` + `overrides` fallback 추가
- **StatsComponent "Cannot set property 0..."**: 런타임 에러. 추적 결과 `RealtimePreviewDynamic` 에서 `component.style` 을 스프레드하다 숫자 키가 섞여 들어옴. `sanitizeStyleKeys` 로 제거
- **DOM warning 폭발**: 10+ 컴포넌트가 모르는 prop 받음. Python 스크립트로 21개에 일괄 `filterDOMProps` 적용
- **임의 제약 추가 금지**: `limit: 100/1000`, `status='published'` 등을 지시 없이 추가했다가 강한 피드백. 모두 제거. 메모리(`feedback_no_arbitrary_limits.md`) 등록

## 현재 TemplateRenderer 반응형 방식

시도 순서: scale transform → flex-flow → 비례 변환 → flex-basis+spacer → **small/large 블록 구분 (현재)**

- **small 블록** (`width < canvas의 25%`, 예: 버튼): 원본 px 유지. center_x 가 canvas 오른쪽이면 `right: N%` 기준, 왼쪽이면 `left: N%` 기준 정렬
- **large 블록** (`width >= 25%`, 예: stats/table): 비율 `width: N%` 로 variance
- **narrow 모드** (`@container (max-width: 640px)`): 전체 세로 스택 + width 100%
- 컴포넌트 간 수직 위치는 `top: (pos.top - rowTop)px` 로 보존

## 참고 메모리

- `feedback_no_arbitrary_limits.md` — 지시 없이 리미트/필터/디폴트 넣지 말 것
- `feedback_responsive_container_based.md` — 반응형은 @container 기준
- `feedback_admin_vs_user_design.md` — 관리자(복잡 OK) vs 사용자(단순/통일)
- `feedback_menu_is_dashboard.md` — 사용자 메뉴 = 대시보드
