35 KiB
레이아웃 추가 가이드
화면관리 시스템에서 새로운 레이아웃을 추가하는 방법을 설명합니다.
📋 목차
🎨 현재 지원하는 레이아웃
기본 레이아웃 (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를 이용한 자동 생성
기본 사용법
# 기본 형태
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로 입력하면 자동으로 변환됩니다:
# 입력: hero-section
📁 디렉토리: hero-section/
🔖 ID: hero-section
📄 클래스명: HeroSection
🔧 변수명: heroSection
✅ 올바른 이름 예시
card-gridside-navigationdata-tablehero-sectionmy-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 - 레이아웃 정의
export const YourLayoutDefinition = createLayoutDefinition({
id: "your-layout",
name: "yourLayout",
nameEng: "Your Layout",
description: "사용자 정의 레이아웃입니다",
category: "basic",
component: YourLayoutWrapper,
defaultConfig: {
/* 기본 설정 */
},
defaultZones: [
/* 기본 영역들 */
],
});
2. YourLayoutLayout.tsx - React 컴포넌트
export const YourLayoutLayout: React.FC<YourLayoutProps> = ({ layout, isDesignMode, renderer, ...props }) => {
// 레이아웃 UI 구현
};
3. YourLayoutRenderer.tsx - 렌더링 로직
export class YourLayoutRenderer extends AutoRegisteringLayoutRenderer {
static layoutDefinition = YourLayoutDefinition;
render(): React.ReactElement {
return <YourLayoutLayout {...this.props} renderer={this} />;
}
}
🎨 레이아웃 커스터마이징
1. 기본 구조 수정
생성된 YourLayoutLayout.tsx에서 레이아웃 구조를 정의합니다:
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에서 레이아웃별 설정을 정의합니다:
export const YourLayoutConfig = {
defaultConfig: {
yourLayout: {
columns: 3, // 열 개수
rows: 2, // 행 개수
gap: 16, // 간격
aspectRatio: "16:9", // 비율
backgroundColor: "#ffffff",
borderRadius: "8px",
},
},
};
3. 타입 정의
types.ts에서 설정 타입을 정의합니다:
export interface YourLayoutConfig {
columns?: number;
rows?: number;
gap?: number;
aspectRatio?: string;
backgroundColor?: string;
borderRadius?: string;
}
export interface YourLayoutProps extends LayoutRendererProps {
renderer: YourLayoutRenderer;
}
🎴 카드 레이아웃 상세 가이드
개요
카드 레이아웃은 데이터 기반 레이아웃으로, 실제 데이터베이스 테이블의 데이터를 카드 형태로 표시합니다. 두 가지 모드로 작동합니다:
- 디자인 모드: 6개의 존이 있는 일반 레이아웃 (편집기에서)
- 실행 모드: 테이블 데이터를 기반으로 한 동적 카드 생성
🔧 설정 방법
1. 테이블 컬럼 매핑
카드의 각 부분에 어떤 테이블 컬럼을 표시할지 설정합니다:
interface CardColumnMapping {
titleColumn?: string; // 카드 제목에 표시할 컬럼
subtitleColumn?: string; // 카드 서브타이틀에 표시할 컬럼
descriptionColumn?: string; // 카드 설명에 표시할 컬럼
imageColumn?: string; // 카드 이미지에 표시할 컬럼
displayColumns?: string[]; // 추가로 표시할 컬럼들
}
2. 카드 스타일 설정
카드의 외관을 세부적으로 조정할 수 있습니다:
interface CardStyle {
showTitle?: boolean; // 타이틀 표시 여부
showSubtitle?: boolean; // 서브타이틀 표시 여부
showDescription?: boolean; // 설명 표시 여부
showImage?: boolean; // 이미지 표시 여부
maxDescriptionLength?: number; // 설명 최대 길이
cardsPerRow?: number; // 한 행당 카드 수
cardSpacing?: number; // 카드 간격 (px)
}
📋 사용 가이드
1. 카드 레이아웃 생성
- 레이아웃 추가: 템플릿에서 "카드 레이아웃" 선택
- 테이블 선택: 화면에서 사용할 테이블 선택
- 컬럼 매핑: 상세설정 패널에서 컬럼 매핑 설정
2. 컬럼 매핑 설정
상세설정 패널에서 다음을 설정합니다:
- 타이틀 컬럼: 카드 제목으로 사용할 컬럼 (예: 상품명, 직원명)
- 서브타이틀 컬럼: 카드 부제목으로 사용할 컬럼 (예: 카테고리, 부서)
- 설명 컬럼: 카드 설명으로 사용할 컬럼 (예: 상세설명, 상태)
- 이미지 컬럼: 카드 이미지로 사용할 컬럼 (이미지 URL)
3. 표시 컬럼 추가
추가로 표시하고 싶은 컬럼들을 동적으로 추가할 수 있습니다:
- "+ 컬럼 추가" 버튼 클릭
- 드롭다운에서 원하는 컬럼 선택
- 각 컬럼마다 "삭제" 버튼으로 제거 가능
4. 카드 스타일 조정
- 한 행당 카드 수: 1-6개 설정 가능
- 카드 간격: 픽셀 단위로 조정
- 표시 옵션: 타이틀, 서브타이틀, 설명, 이미지 개별 제어
- 설명 최대 길이: 긴 설명 텍스트 자동 축약
🎨 카드 렌더링 로직
라벨 우선 표시
컬럼명 대신 라벨값을 우선 표시합니다:
// 컬럼 라벨 변환 함수
const getColumnLabel = (columnName: string) => {
const column = tableColumns.find((col) => col.columnName === columnName);
return column?.columnLabel || columnName; // 라벨이 있으면 라벨, 없으면 컬럼명
};
// 사용 예시
// 컬럼명: "reg_date" → 라벨: "등록일"
// 컬럼명: "user_name" → 라벨: "사용자명"
데이터 가져오기
카드 레이아웃은 다음과 같이 데이터를 가져옵니다:
// 테이블 데이터와 컬럼 정보를 병렬로 로드
const [dataResponse, columnsResponse] = await Promise.all([
tableTypeApi.getTableData(tableName, { page: 1, size: 50 }),
tableTypeApi.getColumns(tableName),
]);
// 카드 렌더링에 사용
setTableData(dataResponse.data);
setTableColumns(columnsResponse);
카드 구조
각 카드는 다음과 같은 구조로 렌더링됩니다:
// 카드 내용 구성
<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. 직원 카드
{
"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. 상품 카드
{
"columnMapping": {
"titleColumn": "product_name", // 상품명
"subtitleColumn": "category_name", // 카테고리
"descriptionColumn": "description", // 상품설명
"imageColumn": "product_image", // 상품 이미지
"displayColumns": ["price", "stock", "rating"] // 가격, 재고, 평점
},
"cardStyle": {
"cardsPerRow": 4,
"cardSpacing": 20,
"maxDescriptionLength": 80
}
}
3. 프로젝트 카드
{
"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 렌더링 최적화
🚨 주의사항
- 성능 고려: 대량 데이터 시 페이징 처리 (현재 최대 50개)
- 이미지 처리: 이미지 URL이 유효하지 않을 경우 대체 이미지 표시
- 텍스트 길이: 긴 텍스트는 자동으로 축약 처리
- 데이터 타입: 날짜/시간 데이터의 경우 적절한 포맷팅 필요
🔧 고급 설정
영역(Zone) 관리 시스템
존과 컴포넌트의 관계
레이아웃의 존(Zone)에 컴포넌트를 배치하면 다음과 같은 관계가 형성됩니다:
// 존에 배치된 컴포넌트의 구조
interface ComponentInZone {
id: string;
parentId: string; // 레이아웃 컴포넌트 ID
zoneId: string; // 존 ID (새로 추가된 필드)
position: { x: number; y: number }; // 존 내부 상대 좌표
// ... 기타 속성들
}
존에 컴포넌트 배치하기
-
드래그앤드롭으로 배치
- 테이블이나 컬럼을 레이아웃의 존 영역에 드롭
- 자동으로
parentId와zoneId가 설정됨 - 존 내부 상대 좌표로 위치 계산
-
레이아웃 이동 시 함께 이동
- 레이아웃 컴포넌트를 이동하면 존에 속한 모든 컴포넌트가 함께 이동
- 상대적 위치 관계 유지
- 드래그 중에도 실시간으로 함께 이동
존 드롭 이벤트 처리
각 존은 독립적인 드롭존으로 작동합니다:
// 존별 드롭 처리 예시
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) 커스터마이징
영역별로 다른 스타일을 적용하려면:
// 영역별 스타일 계산
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;
};
반응형 레이아웃
미디어 쿼리를 사용한 반응형 구현:
const getResponsiveStyle = (): React.CSSProperties => {
return {
display: "grid",
gridTemplateColumns: `repeat(auto-fit, minmax(300px, 1fr))`,
gap: "16px",
// CSS-in-JS에서는 미디어 쿼리를 직접 사용할 수 없으므로
// CSS 클래스나 컨테이너 쿼리 사용 권장
};
};
애니메이션 추가
CSS 애니메이션을 포함한 레이아웃:
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 사용
모든 레이아웃에서 부모 높이를 올바르게 따르도록 하는 패턴:
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 레이아웃
// 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 레이아웃
// 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 레이아웃
// 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 규칙
!important사용: Tailwind나 다른 CSS 프레임워크의 스타일을 오버라이드height: 100%: 부모의 높이를 완전히 따르도록 설정min-height: 최소 높이 보장으로 너무 작아지지 않도록 방지flex: 1: 플렉스 아이템이 사용 가능한 공간을 채우도록 설정
🔄 자동 등록 시스템
Hot Reload 지원
새 레이아웃은 다음과 같이 자동으로 등록됩니다:
- 파일 저장 시: Hot Reload로 즉시 반영
- 자동 등록:
AutoRegisteringLayoutRenderer상속으로 자동 등록 - 즉시 사용: 화면편집기에서 바로 사용 가능
수동 등록 (필요한 경우)
lib/registry/layouts/index.ts에 직접 추가:
// 새 구조 레이아웃들 (자동 등록)
import "./your-layout/YourLayoutRenderer";
🛠️ 문제 해결
자주 발생하는 오류
1. "Cannot read properties of undefined" 오류
// ❌ 문제: 설정이 없을 때 오류
const config = layout.layoutConfig.yourLayout.someProperty;
// ✅ 해결: 안전한 접근
const config = layout.layoutConfig.yourLayout?.someProperty || defaultValue;
2. "React does not recognize prop" 경고
// ❌ 문제: 모든 props를 DOM에 전달
<div {...props}>
// ✅ 해결: DOM props만 전달
const { layout, isDesignMode, renderer, ...domProps } = props;
<div {...domProps}>
3. 레이아웃이 화면편집기에 나타나지 않음
- 파일 저장 확인: 모든 파일이 저장되었는지 확인
- 자동 등록 확인:
YourLayoutRenderer.registerSelf()호출 여부 - 브라우저 새로고침: 캐시 문제일 수 있음
- 개발자 도구:
window.__LAYOUT_REGISTRY__.list()로 등록 상태 확인
4. 레이아웃 높이가 적용되지 않음
// ❌ 문제: 인라인 스타일만 사용
<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 방향 설정이 적용되지 않음
// ❌ 문제: 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. 카드 레이아웃에서 데이터가 표시되지 않음
- 테이블 선택 확인: 화면에서 테이블이 선택되었는지 확인
- 컬럼 매핑 설정: 상세설정에서 최소한 타이틀 컬럼은 설정
- 체크박스 상태: "타이틀 표시", "서브타이틀 표시" 등이 체크되었는지 확인
- API 호출 확인: 브라우저 개발자 도구에서 API 호출 성공 여부 확인
7. React Key Prop 경고
// ❌ 문제: 고유하지 않은 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. 라벨이 컬럼명으로 표시되는 문제
// ❌ 문제: 컬럼명 직접 사용
<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일 해결됨)
// ❌ 문제: 복잡한 존별 드롭 로직으로 인한 오류
- Runtime TypeError: Cannot read properties of undefined (reading 'width')
- 다중선택 기능 중단
- 존별 드롭 이벤트 충돌
// ✅ 해결: 드롭 시스템 완전 단순화
- 모든 존별 드롭 로직 제거
- 일반 캔버스 드롭만 사용
- 레이아웃은 시각적 가이드 역할만
- z-index 기반 레이어 분리 (레이아웃=1, 컴포넌트=2+)
드롭 시스템 단순화 후 장점
- ✅ 안정성: 복잡한 이벤트 체인 제거로 오류 가능성 감소
- ✅ 일관성: 모든 영역에서 동일한 드롭 동작
- ✅ 성능: 불필요한 prop 전달 및 매핑 로직 제거
- ✅ 유지보수: 단순한 구조로 디버깅 및 수정 용이
새로운 레이아웃 개발 시 주의사항
// ✅ 올바른 레이아웃 구현
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>
);
};
디버깅 도구
브라우저 개발자 도구
// 등록된 레이아웃 목록 확인
window.__LAYOUT_REGISTRY__.list();
// 특정 레이아웃 정보 확인
window.__LAYOUT_REGISTRY__.get("your-layout");
// 레지스트리 통계
window.__LAYOUT_REGISTRY__.stats();
📖 예시: 완전한 레이아웃 생성
1. CLI로 생성
node scripts/create-layout.js pricing-table --category=content --zones=4 --description="가격표 레이아웃" --author="개발팀"
2. 생성 결과
✅ 레이아웃 생성 완료!
📁 이름: pricingTable
🔖 ID: pricing-table
📂 카테고리: content
🎯 존 개수: 4
3. 커스터마이징
// 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로 완전한 타입 지원
- 확장 가능성: 새로운 존 타입 쉽게 추가 가능
🔍 사용 예시
대시보드 레이아웃 예시
# 대시보드 레이아웃 생성
node scripts/create-layout.js dashboard-layout --category=dashboard --zones=4 --description="대시보드 레이아웃"
생성된 레이아웃에서:
- 헤더 존: 제목, 네비게이션 컴포넌트 배치
- 사이드바 존: 메뉴, 필터 컴포넌트 배치
- 메인 존: 차트, 테이블 컴포넌트 배치
- 푸터 존: 상태 정보, 액션 버튼 배치
폼 레이아웃 예시
# 폼 레이아웃 생성
node scripts/create-layout.js form-layout --category=form --zones=3 --description="폼 레이아웃"
생성된 레이아웃에서:
- 입력 존: 텍스트 필드, 선택박스 배치
- 첨부 존: 파일 업로드 컴포넌트 배치
- 액션 존: 저장, 취소 버튼 배치
🎯 마무리
새로운 CLI 방식과 존 관리 시스템, 그리고 데이터 기반 레이아웃으로 화면관리 시스템이 혁신적으로 개선되었습니다:
🚀 핵심 기능
- 한 줄 명령어로 모든 파일 자동 생성
- 타입 안전성 보장 (TypeScript 완전 지원)
- 자동 등록으로 즉시 사용 가능
- Hot Reload 지원으로 빠른 개발
- 존 관리 시스템으로 구조적 레이아웃 관리
- 데이터 기반 렌더링 (카드 레이아웃)
- 동적 존 개수 설정 (Flexbox, Grid)
- 높이 자동 적용 (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.widthundefined 오류 완전 해결 - ✅ 다중선택 복구: 드래그로 다중선택 기능 정상화
- ✅ 안정적 드롭: 레이아웃 위든 어디든 일관된 드롭 동작
- ✅ 코드 단순화: 복잡한 존별 로직 제거로 유지보수성 향상
기술적 변경점
- GridLayout, FlexboxLayout:
onDragOver,onDrop이벤트 핸들러 제거 - ScreenDesigner:
onZoneComponentDropprop 전달 제거 - DynamicComponentRenderer:
onComponentDrop매핑 로직 제거 - DynamicLayoutRenderer: 존별 prop 전달 제거
- RealtimePreviewDynamic: z-index 기반 레이어링 적용
개발자 가이드
새로운 시스템에서는:
- 🚫 존별 드롭 로직 구현 금지: 모든 드롭은 캔버스 레벨에서 처리
- ✅ 시각적 가이드만 제공: 레이아웃은 배치 가이드라인 역할만
- ✅ z-index로 레이어 관리: 레이아웃=1, 컴포넌트=2+ 설정
- ✅ 단순한 이벤트 처리: 복잡한 이벤트 체인 대신 직접적인 핸들링
개발자 도구 강화
- ✅ 디버깅 로그: 카드 설정 로드, 데이터 가져오기 등 상세 로깅
- ✅ 에러 핸들링: API 함수명 오류, 데이터 타입 불일치 등 완벽 처리
- ✅ 타입 안전성: 모든 컴포넌트와 설정에 완전한 TypeScript 지원
📈 지원하는 레이아웃 현황
- Grid Layout - 격자 형태 기본 레이아웃
- Flexbox Layout - 유연한 방향 설정 가능
- Split Layout - 좌우/상하 분할
- Accordion Layout - 접기/펼치기 네비게이션
- Card Layout - 데이터 기반 카드 표시 ⭐
- 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()를 실행해보세요! 🚀