ERP-node/frontend/docs/레이아웃_추가_가이드.md

1191 lines
35 KiB
Markdown

# 레이아웃 추가 가이드
화면관리 시스템에서 새로운 레이아웃을 추가하는 방법을 설명합니다.
## 📋 목차
1. [현재 지원하는 레이아웃](#현재-지원하는-레이아웃)
2. [CLI를 이용한 자동 생성](#cli를-이용한-자동-생성)
3. [생성된 파일 구조](#생성된-파일-구조)
4. [레이아웃 커스터마이징](#레이아웃-커스터마이징)
5. [카드 레이아웃 상세 가이드](#카드-레이아웃-상세-가이드)
6. [고급 설정](#고급-설정)
7. [문제 해결](#문제-해결)
---
## 🎨 현재 지원하는 레이아웃
### 기본 레이아웃 (Basic)
#### 1. 그리드 레이아웃 (Grid Layout)
- **ID**: `grid`
- **존 개수**: 4개 (동적 설정 가능)
- **특징**: 격자 형태의 균등 분할 레이아웃
- **용도**: 카드, 대시보드 위젯 배치
#### 2. 플렉스박스 레이아웃 (Flexbox Layout)
- **ID**: `flexbox`
- **존 개수**: 2개 (동적 설정 가능)
- **특징**: 가로/세로 방향 설정 가능, 자동 크기 조정
- **방향 설정**: 수평(`row`) / 수직(`column`)
- **용도**: 반응형 레이아웃, 사이드바 구조
#### 3. 분할 레이아웃 (Split Layout)
- **ID**: `split`
- **존 개수**: 2개
- **특징**: 좌우 또는 상하 분할
- **용도**: 마스터-디테일 화면, 비교 화면
### 네비게이션 레이아웃 (Navigation)
#### 4. 아코디언 레이아웃 (Accordion Layout)
- **ID**: `accordion`
- **존 개수**: 3개
- **특징**: 접을 수 있는 섹션들
- **용도**: FAQ, 설정 패널, 단계별 폼
### 대시보드 레이아웃 (Dashboard)
#### 5. 카드 레이아웃 (Card Layout) ⭐ **데이터 기반**
- **ID**: `card-layout`
- **존 개수**: 6개 (디자인 모드), 데이터 기반 (실행 모드)
- **특징**: 실제 테이블 데이터를 카드 형태로 표시
- **핵심 기능**:
- 테이블 컬럼 매핑 (타이틀, 서브타이틀, 설명, 이미지)
- 동적 표시 컬럼 관리
- 컬럼 라벨 우선 표시
- 실시간 카드 스타일 설정
- 한 행당 카드 수, 간격 조정
- **용도**: 상품 목록, 직원 카드, 프로젝트 카드
### 콘텐츠 레이아웃 (Content)
#### 6. 히어로 섹션 레이아웃 (Hero Section Layout)
- **ID**: `hero-section`
- **존 개수**: 3개
- **특징**: 대형 헤더 섹션과 하위 영역들
- **용도**: 랜딩 페이지, 제품 소개
---
## 🚀 CLI를 이용한 자동 생성
### 기본 사용법
```bash
# 기본 형태
node scripts/create-layout.js <레이아웃이름> [옵션]
# 예시
node scripts/create-layout.js card-grid --category=dashboard --zones=6 --description="카드 형태의 그리드 레이아웃"
```
### 📝 사용 가능한 옵션
| 옵션 | 필수 여부 | 기본값 | 설명 | 예시 |
| --------------- | --------- | ----------- | ----------------- | ----------------------------------- |
| `--category` | 선택 | `basic` | 레이아웃 카테고리 | `--category=dashboard` |
| `--zones` | 선택 | `2` | 영역 개수 | `--zones=4` |
| `--description` | 선택 | 자동 생성 | 레이아웃 설명 | `--description="사이드바 레이아웃"` |
| `--author` | 선택 | `Developer` | 작성자 이름 | `--author="김개발"` |
### 🏷️ 카테고리 종류
| 카테고리 | 설명 | 예시 레이아웃 |
| ------------ | ------------- | ------------------------ |
| `basic` | 기본 레이아웃 | 그리드, 플렉스박스, 분할 |
| `navigation` | 네비게이션 | 탭, 아코디언, 메뉴 |
| `dashboard` | 대시보드 | 카드, 위젯, 차트 |
| `content` | 콘텐츠 | 헤더-본문, 영웅 섹션 |
| `form` | 폼 | 입력 폼, 설정 패널 |
| `table` | 테이블 | 데이터 테이블, 목록 |
### 💡 이름 규칙
레이아웃 이름은 **하이픈(`-`)을 사용한 kebab-case**로 입력하면 자동으로 변환됩니다:
```bash
# 입력: hero-section
📁 디렉토리: hero-section/
🔖 ID: hero-section
📄 클래스명: HeroSection
🔧 변수명: heroSection
```
#### ✅ 올바른 이름 예시
- `card-grid`
- `side-navigation`
- `data-table`
- `hero-section`
- `my-awesome-layout`
#### ❌ 피해야 할 이름
- `CardGrid` (파스칼케이스)
- `card_grid` (스네이크케이스)
- `cardGrid` (카멜케이스)
---
## 📂 생성된 파일 구조
CLI로 레이아웃을 생성하면 다음과 같은 파일들이 자동으로 생성됩니다:
```
lib/registry/layouts/your-layout/
├── index.ts # 레이아웃 정의 및 등록
├── YourLayoutLayout.tsx # React 컴포넌트
├── YourLayoutRenderer.tsx # 렌더링 로직
├── config.ts # 기본 설정
├── types.ts # 타입 정의
└── README.md # 문서
```
### 🔧 각 파일의 역할
#### 1. `index.ts` - 레이아웃 정의
```typescript
export const YourLayoutDefinition = createLayoutDefinition({
id: "your-layout",
name: "yourLayout",
nameEng: "Your Layout",
description: "사용자 정의 레이아웃입니다",
category: "basic",
component: YourLayoutWrapper,
defaultConfig: {
/* 기본 설정 */
},
defaultZones: [
/* 기본 영역들 */
],
});
```
#### 2. `YourLayoutLayout.tsx` - React 컴포넌트
```typescript
export const YourLayoutLayout: React.FC<YourLayoutProps> = ({ layout, isDesignMode, renderer, ...props }) => {
// 레이아웃 UI 구현
};
```
#### 3. `YourLayoutRenderer.tsx` - 렌더링 로직
```typescript
export class YourLayoutRenderer extends AutoRegisteringLayoutRenderer {
static layoutDefinition = YourLayoutDefinition;
render(): React.ReactElement {
return <YourLayoutLayout {...this.props} renderer={this} />;
}
}
```
---
## 🎨 레이아웃 커스터마이징
### 1. 기본 구조 수정
생성된 `YourLayoutLayout.tsx`에서 레이아웃 구조를 정의합니다:
```typescript
export const YourLayoutLayout: React.FC<YourLayoutProps> = ({
layout,
isDesignMode = false,
renderer,
}) => {
const yourLayoutConfig = layout.layoutConfig.yourLayout;
const containerStyle = renderer.getLayoutContainerStyle();
// 레이아웃별 커스텀 스타일
const yourLayoutStyle: React.CSSProperties = {
...containerStyle,
display: "grid",
gridTemplateColumns: "repeat(3, 1fr)",
gridTemplateRows: "repeat(2, 200px)",
gap: "16px",
padding: "16px",
};
return (
<div style={yourLayoutStyle}>
{layout.zones.map((zone) => {
const zoneChildren = renderer.getZoneChildren(zone.id);
return (
<div key={zone.id} className="zone-area">
{/* 존 렌더링 */}
{renderer.renderZone(zone, zoneChildren)}
</div>
);
})}
</div>
);
};
```
### 2. 설정 옵션 추가
`config.ts`에서 레이아웃별 설정을 정의합니다:
```typescript
export const YourLayoutConfig = {
defaultConfig: {
yourLayout: {
columns: 3, // 열 개수
rows: 2, // 행 개수
gap: 16, // 간격
aspectRatio: "16:9", // 비율
backgroundColor: "#ffffff",
borderRadius: "8px",
},
},
};
```
### 3. 타입 정의
`types.ts`에서 설정 타입을 정의합니다:
```typescript
export interface YourLayoutConfig {
columns?: number;
rows?: number;
gap?: number;
aspectRatio?: string;
backgroundColor?: string;
borderRadius?: string;
}
export interface YourLayoutProps extends LayoutRendererProps {
renderer: YourLayoutRenderer;
}
```
---
## 🎴 카드 레이아웃 상세 가이드
### 개요
카드 레이아웃은 **데이터 기반 레이아웃**으로, 실제 데이터베이스 테이블의 데이터를 카드 형태로 표시합니다. 두 가지 모드로 작동합니다:
- **디자인 모드**: 6개의 존이 있는 일반 레이아웃 (편집기에서)
- **실행 모드**: 테이블 데이터를 기반으로 한 동적 카드 생성
### 🔧 설정 방법
#### 1. 테이블 컬럼 매핑
카드의 각 부분에 어떤 테이블 컬럼을 표시할지 설정합니다:
```typescript
interface CardColumnMapping {
titleColumn?: string; // 카드 제목에 표시할 컬럼
subtitleColumn?: string; // 카드 서브타이틀에 표시할 컬럼
descriptionColumn?: string; // 카드 설명에 표시할 컬럼
imageColumn?: string; // 카드 이미지에 표시할 컬럼
displayColumns?: string[]; // 추가로 표시할 컬럼들
}
```
#### 2. 카드 스타일 설정
카드의 외관을 세부적으로 조정할 수 있습니다:
```typescript
interface CardStyle {
showTitle?: boolean; // 타이틀 표시 여부
showSubtitle?: boolean; // 서브타이틀 표시 여부
showDescription?: boolean; // 설명 표시 여부
showImage?: boolean; // 이미지 표시 여부
maxDescriptionLength?: number; // 설명 최대 길이
cardsPerRow?: number; // 한 행당 카드 수
cardSpacing?: number; // 카드 간격 (px)
}
```
### 📋 사용 가이드
#### 1. 카드 레이아웃 생성
1. **레이아웃 추가**: 템플릿에서 "카드 레이아웃" 선택
2. **테이블 선택**: 화면에서 사용할 테이블 선택
3. **컬럼 매핑**: 상세설정 패널에서 컬럼 매핑 설정
#### 2. 컬럼 매핑 설정
**상세설정 패널**에서 다음을 설정합니다:
1. **타이틀 컬럼**: 카드 제목으로 사용할 컬럼 (예: 상품명, 직원명)
2. **서브타이틀 컬럼**: 카드 부제목으로 사용할 컬럼 (예: 카테고리, 부서)
3. **설명 컬럼**: 카드 설명으로 사용할 컬럼 (예: 상세설명, 상태)
4. **이미지 컬럼**: 카드 이미지로 사용할 컬럼 (이미지 URL)
#### 3. 표시 컬럼 추가
추가로 표시하고 싶은 컬럼들을 동적으로 추가할 수 있습니다:
- **"+ 컬럼 추가"** 버튼 클릭
- 드롭다운에서 원하는 컬럼 선택
- 각 컬럼마다 **"삭제"** 버튼으로 제거 가능
#### 4. 카드 스타일 조정
- **한 행당 카드 수**: 1-6개 설정 가능
- **카드 간격**: 픽셀 단위로 조정
- **표시 옵션**: 타이틀, 서브타이틀, 설명, 이미지 개별 제어
- **설명 최대 길이**: 긴 설명 텍스트 자동 축약
### 🎨 카드 렌더링 로직
#### 라벨 우선 표시
컬럼명 대신 **라벨값을 우선 표시**합니다:
```typescript
// 컬럼 라벨 변환 함수
const getColumnLabel = (columnName: string) => {
const column = tableColumns.find((col) => col.columnName === columnName);
return column?.columnLabel || columnName; // 라벨이 있으면 라벨, 없으면 컬럼명
};
// 사용 예시
// 컬럼명: "reg_date" → 라벨: "등록일"
// 컬럼명: "user_name" → 라벨: "사용자명"
```
#### 데이터 가져오기
카드 레이아웃은 다음과 같이 데이터를 가져옵니다:
```typescript
// 테이블 데이터와 컬럼 정보를 병렬로 로드
const [dataResponse, columnsResponse] = await Promise.all([
tableTypeApi.getTableData(tableName, { page: 1, size: 50 }),
tableTypeApi.getColumns(tableName),
]);
// 카드 렌더링에 사용
setTableData(dataResponse.data);
setTableColumns(columnsResponse);
```
#### 카드 구조
각 카드는 다음과 같은 구조로 렌더링됩니다:
```typescript
// 카드 내용 구성
<div className="card">
{/* 이미지 (설정된 경우) */}
{showImage && imageColumn && (
<img src={getColumnValue(item, imageColumn)} />
)}
{/* 타이틀 */}
{showTitle && titleColumn && (
<h3>{getColumnValue(item, titleColumn)}</h3>
)}
{/* 서브타이틀 */}
{showSubtitle && subtitleColumn && (
<p>{getColumnValue(item, subtitleColumn)}</p>
)}
{/* 설명 */}
{showDescription && descriptionColumn && (
<p>{truncateText(getColumnValue(item, descriptionColumn), maxLength)}</p>
)}
{/* 추가 표시 컬럼들 */}
{displayColumns?.map(columnName => (
<div key={columnName}>
<span>{getColumnLabel(columnName)}:</span>
<span>{getColumnValue(item, columnName)}</span>
</div>
))}
</div>
```
### 💡 활용 예시
#### 1. 직원 카드
```json
{
"columnMapping": {
"titleColumn": "emp_name", // 직원명
"subtitleColumn": "dept_name", // 부서명
"descriptionColumn": "position", // 직책
"imageColumn": "profile_image", // 프로필 사진
"displayColumns": ["email", "phone", "hire_date"] // 이메일, 전화번호, 입사일
},
"cardStyle": {
"cardsPerRow": 3,
"cardSpacing": 16,
"showTitle": true,
"showSubtitle": true,
"showDescription": true,
"showImage": true
}
}
```
#### 2. 상품 카드
```json
{
"columnMapping": {
"titleColumn": "product_name", // 상품명
"subtitleColumn": "category_name", // 카테고리
"descriptionColumn": "description", // 상품설명
"imageColumn": "product_image", // 상품 이미지
"displayColumns": ["price", "stock", "rating"] // 가격, 재고, 평점
},
"cardStyle": {
"cardsPerRow": 4,
"cardSpacing": 20,
"maxDescriptionLength": 80
}
}
```
#### 3. 프로젝트 카드
```json
{
"columnMapping": {
"titleColumn": "project_name", // 프로젝트명
"subtitleColumn": "status", // 상태
"descriptionColumn": "description", // 프로젝트 설명
"displayColumns": ["start_date", "end_date", "manager", "progress"] // 시작일, 종료일, 담당자, 진행률
},
"cardStyle": {
"cardsPerRow": 2,
"cardSpacing": 24,
"showImage": false
}
}
```
### 🔍 주요 특징
#### 1. **실시간 설정 반영**
- 상세설정 패널에서 설정 변경 시 즉시 카드에 반영
- 체크박스로 표시 요소 실시간 제어
- 컬럼 매핑 변경 시 즉시 업데이트
#### 2. **라벨 우선 표시 시스템**
- 데이터베이스 컬럼명 대신 사용자 친화적인 라벨 표시
- 라벨이 없는 경우 컬럼명으로 대체
- 드롭다운에서도 "라벨명 (데이터타입)" 형식으로 표시
#### 3. **동적 컬럼 관리**
- 표시할 컬럼을 자유롭게 추가/제거
- 각 컬럼별 개별 라벨 표시
- 컬럼 순서에 따른 표시 순서 보장
#### 4. **반응형 카드 레이아웃**
- 한 행당 카드 수 조정으로 반응형 구현
- 카드 간격 픽셀 단위 조정
- 자동 높이 조정 및 최소 높이 보장
#### 5. **데이터 안전성**
- 빈 데이터에 대한 폴백 처리
- 잘못된 컬럼 참조 시 오류 방지
- 고유한 key prop으로 React 렌더링 최적화
### 🚨 주의사항
1. **성능 고려**: 대량 데이터 시 페이징 처리 (현재 최대 50개)
2. **이미지 처리**: 이미지 URL이 유효하지 않을 경우 대체 이미지 표시
3. **텍스트 길이**: 긴 텍스트는 자동으로 축약 처리
4. **데이터 타입**: 날짜/시간 데이터의 경우 적절한 포맷팅 필요
---
## 🔧 고급 설정
### 영역(Zone) 관리 시스템
#### 존과 컴포넌트의 관계
레이아웃의 존(Zone)에 컴포넌트를 배치하면 다음과 같은 관계가 형성됩니다:
```typescript
// 존에 배치된 컴포넌트의 구조
interface ComponentInZone {
id: string;
parentId: string; // 레이아웃 컴포넌트 ID
zoneId: string; // 존 ID (새로 추가된 필드)
position: { x: number; y: number }; // 존 내부 상대 좌표
// ... 기타 속성들
}
```
#### 존에 컴포넌트 배치하기
1. **드래그앤드롭으로 배치**
- 테이블이나 컬럼을 레이아웃의 존 영역에 드롭
- 자동으로 `parentId``zoneId`가 설정됨
- 존 내부 상대 좌표로 위치 계산
2. **레이아웃 이동 시 함께 이동**
- 레이아웃 컴포넌트를 이동하면 존에 속한 모든 컴포넌트가 함께 이동
- 상대적 위치 관계 유지
- 드래그 중에도 실시간으로 함께 이동
#### 존 드롭 이벤트 처리
각 존은 독립적인 드롭존으로 작동합니다:
```typescript
// 존별 드롭 처리 예시
const handleZoneDrop = (e: React.DragEvent, zoneId: string) => {
const { type, table, column } = JSON.parse(e.dataTransfer.getData("application/json"));
const newComponent = {
id: generateId(),
type: "widget",
parentId: layoutId, // 레이아웃을 부모로 설정
zoneId: zoneId, // 존 ID 설정
position: {
// 존 내부 상대 좌표
x: e.clientX - dropZone.getBoundingClientRect().left,
y: e.clientY - dropZone.getBoundingClientRect().top,
},
// ... 기타 설정
};
};
```
### 영역(Zone) 커스터마이징
영역별로 다른 스타일을 적용하려면:
```typescript
// 영역별 스타일 계산
const getZoneStyle = (zone: LayoutZone, index: number): React.CSSProperties => {
const baseStyle = {
backgroundColor: "#f8f9fa",
border: "1px solid #e9ecef",
borderRadius: "4px",
padding: "12px",
};
// 첫 번째 영역은 다른 스타일
if (index === 0) {
return {
...baseStyle,
backgroundColor: "#e3f2fd",
gridColumn: "1 / -1", // 전체 너비
};
}
return baseStyle;
};
```
### 반응형 레이아웃
미디어 쿼리를 사용한 반응형 구현:
```typescript
const getResponsiveStyle = (): React.CSSProperties => {
return {
display: "grid",
gridTemplateColumns: `repeat(auto-fit, minmax(300px, 1fr))`,
gap: "16px",
// CSS-in-JS에서는 미디어 쿼리를 직접 사용할 수 없으므로
// CSS 클래스나 컨테이너 쿼리 사용 권장
};
};
```
### 애니메이션 추가
CSS 애니메이션을 포함한 레이아웃:
```typescript
const animatedStyle: React.CSSProperties = {
transition: "all 0.3s ease-in-out",
opacity: isDesignMode ? 0.9 : 1,
transform: isDesignMode ? "scale(0.98)" : "scale(1)",
};
```
### 높이 설정 및 styled-jsx 사용
모든 레이아웃에서 부모 높이를 올바르게 따르도록 하는 패턴:
```typescript
export const YourLayoutLayout: React.FC<YourLayoutProps> = ({ layout, isDesignMode, ...props }) => {
return (
<div className="layout-container">
{/* 레이아웃 컨텐츠 */}
<style jsx>{`
.layout-container {
height: 100% !important;
min-height: 200px !important;
width: 100% !important;
display: flex; /* 또는 grid */
flex-direction: column; /* 필요에 따라 */
}
.zone-area {
flex: 1; /* 존이 사용 가능한 공간을 채우도록 */
height: 100%; /* 그리드 레이아웃의 경우 */
}
`}</style>
</div>
);
};
```
#### 레이아웃별 높이 적용 패턴
**1. Flexbox 레이아웃**
```typescript
// FlexboxLayout.tsx
<style jsx>{`
.flexbox-container {
height: 100% !important;
min-height: 200px !important;
width: 100% !important;
display: flex !important;
flex-direction: ${flexDirection === "row" ? "row" : "column"} !important;
}
.flexbox-zone {
flex: 1;
min-height: 0; /* flexbox 오버플로우 방지 */
}
`}</style>
```
**2. Grid 레이아웃**
```typescript
// GridLayout.tsx
<style jsx>{`
.grid-container {
height: 100% !important;
min-height: 200px !important;
width: 100% !important;
display: grid !important;
grid-template-columns: repeat(${columns}, 1fr);
grid-template-rows: repeat(${rows}, 1fr);
}
.grid-zone {
height: 100%;
width: 100%;
}
`}</style>
```
**3. Card 레이아웃**
```typescript
// CardLayoutLayout.tsx
<style jsx>{`
.card-container {
height: 100% !important;
min-height: 200px !important;
width: 100% !important;
display: grid;
grid-template-columns: repeat(${cardsPerRow}, 1fr);
gap: ${cardSpacing}px;
}
`}</style>
```
#### 중요한 CSS 규칙
1. **`!important` 사용**: Tailwind나 다른 CSS 프레임워크의 스타일을 오버라이드
2. **`height: 100%`**: 부모의 높이를 완전히 따르도록 설정
3. **`min-height`**: 최소 높이 보장으로 너무 작아지지 않도록 방지
4. **`flex: 1`**: 플렉스 아이템이 사용 가능한 공간을 채우도록 설정
---
## 🔄 자동 등록 시스템
### Hot Reload 지원
새 레이아웃은 다음과 같이 자동으로 등록됩니다:
1. **파일 저장 시**: Hot Reload로 즉시 반영
2. **자동 등록**: `AutoRegisteringLayoutRenderer` 상속으로 자동 등록
3. **즉시 사용**: 화면편집기에서 바로 사용 가능
### 수동 등록 (필요한 경우)
`lib/registry/layouts/index.ts`에 직접 추가:
```typescript
// 새 구조 레이아웃들 (자동 등록)
import "./your-layout/YourLayoutRenderer";
```
---
## 🛠️ 문제 해결
### 자주 발생하는 오류
#### 1. "Cannot read properties of undefined" 오류
```typescript
// ❌ 문제: 설정이 없을 때 오류
const config = layout.layoutConfig.yourLayout.someProperty;
// ✅ 해결: 안전한 접근
const config = layout.layoutConfig.yourLayout?.someProperty || defaultValue;
```
#### 2. "React does not recognize prop" 경고
```typescript
// ❌ 문제: 모든 props를 DOM에 전달
<div {...props}>
// ✅ 해결: DOM props만 전달
const { layout, isDesignMode, renderer, ...domProps } = props;
<div {...domProps}>
```
#### 3. 레이아웃이 화면편집기에 나타나지 않음
1. **파일 저장 확인**: 모든 파일이 저장되었는지 확인
2. **자동 등록 확인**: `YourLayoutRenderer.registerSelf()` 호출 여부
3. **브라우저 새로고침**: 캐시 문제일 수 있음
4. **개발자 도구**: `window.__LAYOUT_REGISTRY__.list()` 로 등록 상태 확인
#### 4. 레이아웃 높이가 적용되지 않음
```typescript
// ❌ 문제: 인라인 스타일만 사용
<div style={{ height: "100%" }}>
// ✅ 해결: styled-jsx로 강제 적용
<div className="layout-container">
<style jsx>{`
.layout-container {
height: 100% !important;
min-height: 200px !important;
}
`}</style>
</div>
```
#### 5. Flexbox 방향 설정이 적용되지 않음
```typescript
// ❌ 문제: display: block이 flex를 오버라이드
<div style={{ display: "flex", flexDirection: "row" }}>
// ✅ 해결: !important로 강제 적용
<style jsx>{`
.flexbox-container {
display: flex !important;
flex-direction: ${direction === "row" ? "row" : "column"} !important;
}
`}</style>
```
#### 6. 카드 레이아웃에서 데이터가 표시되지 않음
1. **테이블 선택 확인**: 화면에서 테이블이 선택되었는지 확인
2. **컬럼 매핑 설정**: 상세설정에서 최소한 타이틀 컬럼은 설정
3. **체크박스 상태**: "타이틀 표시", "서브타이틀 표시" 등이 체크되었는지 확인
4. **API 호출 확인**: 브라우저 개발자 도구에서 API 호출 성공 여부 확인
#### 7. React Key Prop 경고
```typescript
// ❌ 문제: 고유하지 않은 key
{items.map((item, index) => (
<div key={item.id}> // item.id가 undefined일 수 있음
// ✅ 해결: 안전한 key 생성
{items.map((item, index) => (
<div key={item.objid || item.id || item.company_code || `item-${index}`}>
```
#### 8. 라벨이 컬럼명으로 표시되는 문제
```typescript
// ❌ 문제: 컬럼명 직접 사용
<span>{columnName}:</span>
// ✅ 해결: 라벨 우선 표시
const getColumnLabel = (columnName: string) => {
const column = tableColumns.find(col => col.columnName === columnName);
return column?.columnLabel || columnName;
};
<span>{getColumnLabel(columnName)}:</span>
```
#### 9. 레이아웃 드롭 시 오류 발생 문제 (9월 11일 해결됨)
```typescript
// ❌ 문제: 복잡한 존별 드롭 로직으로 인한 오류
- Runtime TypeError: Cannot read properties of undefined (reading 'width')
- 다중선택 기능 중단
- 존별 드롭 이벤트 충돌
// ✅ 해결: 드롭 시스템 완전 단순화
- 모든 존별 드롭 로직 제거
- 일반 캔버스 드롭만 사용
- 레이아웃은 시각적 가이드 역할만
- z-index 기반 레이어 분리 (레이아웃=1, 컴포넌트=2+)
```
##### 드롭 시스템 단순화 후 장점
-**안정성**: 복잡한 이벤트 체인 제거로 오류 가능성 감소
-**일관성**: 모든 영역에서 동일한 드롭 동작
-**성능**: 불필요한 prop 전달 및 매핑 로직 제거
-**유지보수**: 단순한 구조로 디버깅 및 수정 용이
##### 새로운 레이아웃 개발 시 주의사항
```typescript
// ✅ 올바른 레이아웃 구현
export const YourLayoutLayout: React.FC<YourLayoutProps> = ({ layout, isDesignMode, ...props }) => {
// 🚫 존별 드롭 이벤트 구현 금지
// onDrop, onDragOver 등 드롭 관련 이벤트 추가하지 않음
return (
<div className="your-layout">
{layout.zones.map((zone) => (
<div
key={zone.id}
className="zone-area"
// 🚫 드롭 이벤트 추가 금지
// onDrop={...}
// onDragOver={...}
>
{/* 존 내용 */}
</div>
))}
<style jsx>{`
.your-layout {
/* z-index는 1로 고정 (레이아웃 레이어) */
z-index: 1;
height: 100% !important;
}
`}</style>
</div>
);
};
```
### 디버깅 도구
#### 브라우저 개발자 도구
```javascript
// 등록된 레이아웃 목록 확인
window.__LAYOUT_REGISTRY__.list();
// 특정 레이아웃 정보 확인
window.__LAYOUT_REGISTRY__.get("your-layout");
// 레지스트리 통계
window.__LAYOUT_REGISTRY__.stats();
```
---
## 📖 예시: 완전한 레이아웃 생성
### 1. CLI로 생성
```bash
node scripts/create-layout.js pricing-table --category=content --zones=4 --description="가격표 레이아웃" --author="개발팀"
```
### 2. 생성 결과
```
✅ 레이아웃 생성 완료!
📁 이름: pricingTable
🔖 ID: pricing-table
📂 카테고리: content
🎯 존 개수: 4
```
### 3. 커스터마이징
```typescript
// PricingTableLayout.tsx
export const PricingTableLayout: React.FC<PricingTableLayoutProps> = ({
layout,
isDesignMode,
renderer,
}) => {
const pricingTableStyle: React.CSSProperties = {
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))",
gap: "24px",
padding: "32px",
backgroundColor: "#f8f9fa",
};
return (
<div style={pricingTableStyle}>
{layout.zones.map((zone, index) => (
<div
key={zone.id}
className={`pricing-card ${index === 1 ? 'featured' : ''}`}
style={{
backgroundColor: "white",
borderRadius: "12px",
padding: "24px",
boxShadow: index === 1 ? "0 8px 32px rgba(0,0,0,0.1)" : "0 2px 8px rgba(0,0,0,0.1)",
border: index === 1 ? "2px solid #3b82f6" : "1px solid #e5e7eb",
transform: index === 1 ? "scale(1.05)" : "scale(1)",
}}
>
{renderer.renderZone(zone, renderer.getZoneChildren(zone.id))}
</div>
))}
</div>
);
};
```
### 4. 즉시 사용 가능
레이아웃이 자동으로 등록되어 화면편집기에서 바로 사용할 수 있습니다!
---
## 🎯 존 관리 시스템의 장점
### 1. 구조적 레이아웃 관리
- **논리적 그룹화**: 관련된 컴포넌트들을 존별로 그룹화
- **일관된 이동**: 레이아웃 이동 시 모든 하위 컴포넌트가 함께 이동
- **상대적 위치**: 존 내부에서의 상대적 위치 관계 유지
### 2. 직관적인 사용자 경험
- **드래그앤드롭**: 존 영역에 직접 드롭하여 배치
- **시각적 피드백**: 존 경계와 라벨로 명확한 구분
- **실시간 미리보기**: 드래그 중에도 함께 이동하는 모습 확인
### 3. 개발자 친화적
- **자동 관계 설정**: `parentId``zoneId` 자동 할당
- **타입 안전성**: TypeScript로 완전한 타입 지원
- **확장 가능성**: 새로운 존 타입 쉽게 추가 가능
## 🔍 사용 예시
### 대시보드 레이아웃 예시
```bash
# 대시보드 레이아웃 생성
node scripts/create-layout.js dashboard-layout --category=dashboard --zones=4 --description="대시보드 레이아웃"
```
생성된 레이아웃에서:
1. **헤더 존**: 제목, 네비게이션 컴포넌트 배치
2. **사이드바 존**: 메뉴, 필터 컴포넌트 배치
3. **메인 존**: 차트, 테이블 컴포넌트 배치
4. **푸터 존**: 상태 정보, 액션 버튼 배치
### 폼 레이아웃 예시
```bash
# 폼 레이아웃 생성
node scripts/create-layout.js form-layout --category=form --zones=3 --description="폼 레이아웃"
```
생성된 레이아웃에서:
1. **입력 존**: 텍스트 필드, 선택박스 배치
2. **첨부 존**: 파일 업로드 컴포넌트 배치
3. **액션 존**: 저장, 취소 버튼 배치
## 🎯 마무리
새로운 CLI 방식과 존 관리 시스템, 그리고 데이터 기반 레이아웃으로 화면관리 시스템이 혁신적으로 개선되었습니다:
### 🚀 핵심 기능
1. **한 줄 명령어**로 모든 파일 자동 생성
2. **타입 안전성** 보장 (TypeScript 완전 지원)
3. **자동 등록**으로 즉시 사용 가능
4. **Hot Reload** 지원으로 빠른 개발
5. **존 관리 시스템**으로 구조적 레이아웃 관리
6. **데이터 기반 렌더링** (카드 레이아웃)
7. **동적 존 개수 설정** (Flexbox, Grid)
8. **높이 자동 적용** (styled-jsx + !important)
### 🎨 사용자 경험
- **직관적 드래그앤드롭**: 존에 직접 컴포넌트 배치
- **일관된 이동**: 레이아웃과 하위 컴포넌트가 함께 이동
- **시각적 피드백**: 명확한 존 경계와 라벨 표시
- **실시간 설정**: 체크박스로 즉시 표시 제어
- **라벨 우선 표시**: 컬럼명 대신 사용자 친화적 라벨
### 🔧 개발자 경험
- **자동화된 설정**: 복잡한 관계 설정이 자동으로 처리
- **확장 가능한 구조**: 새로운 레이아웃 타입 쉽게 추가
- **완전한 타입 지원**: TypeScript로 안전한 개발
- **문제 해결 가이드**: 자주 발생하는 문제들의 해결책 제공
- **성능 최적화**: React key, 메모이제이션 등 적용
### 🆕 최신 업데이트 (2025.09.11)
#### 카드 레이아웃 고도화
-**실제 데이터 연동**: 테이블 데이터를 실시간으로 카드에 표시
-**컬럼 매핑 시스템**: 타이틀, 서브타이틀, 설명 등 자유로운 매핑
-**동적 표시 컬럼**: 필요한 컬럼을 추가/제거하며 동적 관리
-**라벨 우선 표시**: 데이터베이스 컬럼명 대신 사용자 친화적 라벨
-**실시간 설정 반영**: 체크박스 토글로 즉시 카드 업데이트
#### 레이아웃 시스템 개선
-**높이 적용 문제 해결**: 모든 레이아웃에서 부모 높이 완벽 적용
-**Flexbox 방향 설정**: 수평/수직 방향 정상 작동
-**동적 존 개수**: Grid, Flexbox에서 존 개수 실시간 조정
-**시각적 피드백**: 모든 레이아웃에서 존 경계 명확히 표시
-**React 경고 해결**: Key prop, DOM prop 전달 등 모든 경고 해결
#### 🔧 드롭 시스템 대폭 단순화 (9월 11일 추가)
기존의 복잡한 존별 드롭 시스템을 완전히 제거하고 단순한 캔버스 드롭 방식으로 통일했습니다:
##### 변경 사항
-**복잡한 드롭 로직 제거**: `onZoneComponentDrop`, `handleZoneComponentDrop` 등 모든 존별 드롭 이벤트 제거
-**일반 캔버스 드롭만 사용**: 모든 드롭이 `handleComponentDrop`으로 통일
-**레이아웃은 시각적 가이드 역할**: 레이아웃 존은 배치 가이드라인 역할만 수행
-**z-index 기반 레이어링**: 레이아웃 z-index=1, 컴포넌트 z-index=2+로 설정
-**prop 전달 체인 단순화**: 불필요한 prop 매핑 및 전달 로직 제거
##### 새로운 동작 방식
**이전 (복잡한 방식)**:
```
컴포넌트 드래그 → 레이아웃 존 감지 → 존별 드롭 이벤트 → 복잡한 매핑 → 오류 발생
```
**현재 (단순한 방식)**:
```
컴포넌트 드래그 → 캔버스에 드롭 → 일반 handleComponentDrop만 실행 → 안정적 동작
```
##### 해결된 문제들
-**Runtime TypeError 해결**: `selectedComponent.size.width` undefined 오류 완전 해결
-**다중선택 복구**: 드래그로 다중선택 기능 정상화
-**안정적 드롭**: 레이아웃 위든 어디든 일관된 드롭 동작
-**코드 단순화**: 복잡한 존별 로직 제거로 유지보수성 향상
##### 기술적 변경점
1. **GridLayout, FlexboxLayout**: `onDragOver`, `onDrop` 이벤트 핸들러 제거
2. **ScreenDesigner**: `onZoneComponentDrop` prop 전달 제거
3. **DynamicComponentRenderer**: `onComponentDrop` 매핑 로직 제거
4. **DynamicLayoutRenderer**: 존별 prop 전달 제거
5. **RealtimePreviewDynamic**: z-index 기반 레이어링 적용
##### 개발자 가이드
새로운 시스템에서는:
- 🚫 **존별 드롭 로직 구현 금지**: 모든 드롭은 캔버스 레벨에서 처리
-**시각적 가이드만 제공**: 레이아웃은 배치 가이드라인 역할만
-**z-index로 레이어 관리**: 레이아웃=1, 컴포넌트=2+ 설정
-**단순한 이벤트 처리**: 복잡한 이벤트 체인 대신 직접적인 핸들링
#### 개발자 도구 강화
-**디버깅 로그**: 카드 설정 로드, 데이터 가져오기 등 상세 로깅
-**에러 핸들링**: API 함수명 오류, 데이터 타입 불일치 등 완벽 처리
-**타입 안전성**: 모든 컴포넌트와 설정에 완전한 TypeScript 지원
### 📈 지원하는 레이아웃 현황
1. **Grid Layout** - 격자 형태 기본 레이아웃
2. **Flexbox Layout** - 유연한 방향 설정 가능
3. **Split Layout** - 좌우/상하 분할
4. **Accordion Layout** - 접기/펼치기 네비게이션
5. **Card Layout** - 데이터 기반 카드 표시 ⭐
6. **Hero Section Layout** - 대형 헤더 섹션
### 🔮 향후 계획
#### 새로운 레이아웃 타입
- **Table Layout**: 데이터 테이블 전용 레이아웃
- **Form Layout**: 폼 입력에 최적화된 레이아웃
- **Dashboard Layout**: 위젯 배치에 특화된 레이아웃
- **Mobile Responsive**: 모바일 대응 반응형 레이아웃
#### 시스템 개선
- **레이아웃 테마 시스템**: 다크/라이트 모드 지원
- **레이아웃 스타일 프리셋**: 미리 정의된 스타일 템플릿
- **레이아웃 애니메이션**: 전환 효과 및 인터랙션 개선
- **성능 최적화**: 가상화 및 지연 로딩 적용
#### 개발자 도구
- **레이아웃 빌더 GUI**: 코드 없이 레이아웃 생성 도구
- **실시간 프리뷰**: 레이아웃 편집 중 실시간 미리보기
- **레이아웃 디버거**: 시각적 디버깅 도구
- **성능 모니터링**: 레이아웃 렌더링 성능 분석
### 🎯 중요한 변화: 단순화된 드롭 시스템
**2025년 9월 11일**부터 모든 레이아웃에서 **복잡한 존별 드롭 로직이 완전히 제거**되었습니다.
새로운 시스템의 핵심 원칙:
- 🎯 **레이아웃 = 시각적 가이드**: 배치 참고용으로만 사용
- 🎯 **캔버스 = 실제 배치**: 모든 컴포넌트는 캔버스에 자유롭게 배치
- 🎯 **z-index = 레이어 분리**: 레이아웃(1) 위에 컴포넌트(2+) 배치
- 🎯 **단순함 = 안정성**: 복잡한 로직 제거로 오류 최소화
이 변화로 인해:
- ✅ 모든 드롭 관련 오류 해결
- ✅ 다중선택 기능 정상화
- ✅ 레이아웃 개발이 더욱 단순해짐
- ✅ 시스템 전체 안정성 크게 향상
더 자세한 정보가 필요하면 각 레이아웃의 `README.md` 파일을 참고하거나, 브라우저 개발자 도구에서 `window.__LAYOUT_REGISTRY__.help()`를 실행해보세요! 🚀