526 lines
12 KiB
Markdown
526 lines
12 KiB
Markdown
# 화면 임베딩 시스템 - 기존 시스템 충돌 분석 보고서
|
|
|
|
## 📋 분석 개요
|
|
|
|
새로 구현한 **화면 임베딩 및 데이터 전달 시스템**이 기존 화면 관리 시스템과 충돌할 가능성을 분석합니다.
|
|
|
|
---
|
|
|
|
## ✅ 충돌 없음 (안전한 부분)
|
|
|
|
### 1. 데이터베이스 스키마
|
|
|
|
#### 새로운 테이블 (독립적)
|
|
|
|
```sql
|
|
- screen_embedding (신규)
|
|
- screen_data_transfer (신규)
|
|
- screen_split_panel (신규)
|
|
```
|
|
|
|
**충돌 없는 이유**:
|
|
|
|
- ✅ 완전히 새로운 테이블명
|
|
- ✅ 기존 테이블과 이름 중복 없음
|
|
- ✅ 외래키는 기존 `screen_definitions`만 참조 (읽기 전용)
|
|
|
|
#### 기존 테이블 (영향 없음)
|
|
|
|
```sql
|
|
- screen_definitions (변경 없음)
|
|
- screen_layouts (변경 없음)
|
|
- screen_widgets (변경 없음)
|
|
- screen_templates (변경 없음)
|
|
- screen_menu_assignments (변경 없음)
|
|
```
|
|
|
|
**확인 사항**:
|
|
|
|
- ✅ 기존 테이블 구조 변경 없음
|
|
- ✅ 기존 데이터 마이그레이션 불필요
|
|
- ✅ 기존 쿼리 영향 없음
|
|
|
|
---
|
|
|
|
### 2. API 엔드포인트
|
|
|
|
#### 새로운 엔드포인트 (독립적)
|
|
|
|
```
|
|
POST /api/screen-embedding
|
|
GET /api/screen-embedding
|
|
PUT /api/screen-embedding/:id
|
|
DELETE /api/screen-embedding/:id
|
|
|
|
POST /api/screen-data-transfer
|
|
GET /api/screen-data-transfer
|
|
PUT /api/screen-data-transfer/:id
|
|
DELETE /api/screen-data-transfer/:id
|
|
|
|
POST /api/screen-split-panel
|
|
GET /api/screen-split-panel/:screenId
|
|
PUT /api/screen-split-panel/:id
|
|
DELETE /api/screen-split-panel/:id
|
|
```
|
|
|
|
**충돌 없는 이유**:
|
|
|
|
- ✅ 기존 `/api/screen-management/*` 와 다른 경로
|
|
- ✅ 새로운 라우트 추가만 (기존 라우트 수정 없음)
|
|
- ✅ 독립적인 컨트롤러 파일
|
|
|
|
#### 기존 엔드포인트 (영향 없음)
|
|
|
|
```
|
|
/api/screen-management/* (변경 없음)
|
|
/api/screen/* (변경 없음)
|
|
/api/layouts/* (변경 없음)
|
|
```
|
|
|
|
---
|
|
|
|
### 3. TypeScript 타입
|
|
|
|
#### 새로운 타입 파일 (독립적)
|
|
|
|
```typescript
|
|
frontend / types / screen - embedding.ts(신규);
|
|
```
|
|
|
|
**충돌 없는 이유**:
|
|
|
|
- ✅ 기존 `screen.ts`, `screen-management.ts` 와 별도 파일
|
|
- ✅ 타입명 중복 없음
|
|
- ✅ 독립적인 네임스페이스
|
|
|
|
#### 기존 타입 (영향 없음)
|
|
|
|
```typescript
|
|
frontend/types/screen.ts (변경 없음)
|
|
frontend/types/screen-management.ts (변경 없음)
|
|
backend-node/src/types/screen.ts (변경 없음)
|
|
```
|
|
|
|
---
|
|
|
|
### 4. 프론트엔드 컴포넌트
|
|
|
|
#### 새로운 컴포넌트 (독립적)
|
|
|
|
```
|
|
frontend/components/screen-embedding/
|
|
├── EmbeddedScreen.tsx (신규)
|
|
├── ScreenSplitPanel.tsx (신규)
|
|
└── index.ts (신규)
|
|
```
|
|
|
|
**충돌 없는 이유**:
|
|
|
|
- ✅ 별도 디렉토리 (`screen-embedding/`)
|
|
- ✅ 기존 컴포넌트 수정 없음
|
|
- ✅ 독립적으로 import 가능
|
|
|
|
#### 기존 컴포넌트 (영향 없음)
|
|
|
|
```
|
|
frontend/components/screen/ (변경 없음)
|
|
frontend/app/(main)/screens/[screenId]/page.tsx (변경 없음)
|
|
```
|
|
|
|
---
|
|
|
|
## ⚠️ 주의 필요 (잠재적 충돌 가능성)
|
|
|
|
### 1. screen_definitions 테이블 참조
|
|
|
|
**현재 구조**:
|
|
|
|
```sql
|
|
-- 새 테이블들이 screen_definitions를 참조
|
|
CONSTRAINT fk_parent_screen FOREIGN KEY (parent_screen_id)
|
|
REFERENCES screen_definitions(screen_id) ON DELETE CASCADE
|
|
```
|
|
|
|
**잠재적 문제**:
|
|
|
|
- ⚠️ 기존 화면 삭제 시 임베딩 설정도 함께 삭제됨 (CASCADE)
|
|
- ⚠️ 화면 ID 변경 시 임베딩 설정이 깨질 수 있음
|
|
|
|
**해결 방법**:
|
|
|
|
```sql
|
|
-- 이미 구현됨: ON DELETE CASCADE
|
|
-- 화면 삭제 시 자동으로 관련 임베딩도 삭제
|
|
-- 추가 조치 불필요
|
|
```
|
|
|
|
**권장 사항**:
|
|
|
|
- ✅ 화면 삭제 전 임베딩 사용 여부 확인 UI 추가 (Phase 6)
|
|
- ✅ 삭제 시 경고 메시지 표시
|
|
|
|
---
|
|
|
|
### 2. 화면 렌더링 로직
|
|
|
|
**현재 화면 렌더링**:
|
|
|
|
```typescript
|
|
// frontend/app/(main)/screens/[screenId]/page.tsx
|
|
function ScreenViewPage() {
|
|
// 기존: 단일 화면 렌더링
|
|
const screenId = parseInt(params.screenId as string);
|
|
|
|
// 레이아웃 로드
|
|
const layout = await screenApi.getScreenLayout(screenId);
|
|
|
|
// 컴포넌트 렌더링
|
|
<DynamicComponentRenderer components={layout.components} />;
|
|
}
|
|
```
|
|
|
|
**새로운 렌더링 (분할 패널)**:
|
|
|
|
```typescript
|
|
// 분할 패널 화면인 경우
|
|
if (isSplitPanelScreen) {
|
|
const config = await getScreenSplitPanel(screenId);
|
|
return <ScreenSplitPanel config={config} />;
|
|
}
|
|
|
|
// 일반 화면인 경우
|
|
return <DynamicComponentRenderer components={layout.components} />;
|
|
```
|
|
|
|
**잠재적 문제**:
|
|
|
|
- ⚠️ 화면 타입 구분 로직 필요
|
|
- ⚠️ 기존 화면 렌더링 로직 수정 필요
|
|
|
|
**해결 방법**:
|
|
|
|
```typescript
|
|
// 1. screen_definitions에 screen_type 컬럼 추가 (선택사항)
|
|
ALTER TABLE screen_definitions ADD COLUMN screen_type VARCHAR(20) DEFAULT 'normal';
|
|
-- 'normal', 'split_panel', 'embedded'
|
|
|
|
// 2. 또는 screen_split_panel 존재 여부로 판단
|
|
const splitPanelConfig = await getScreenSplitPanel(screenId);
|
|
if (splitPanelConfig.success && splitPanelConfig.data) {
|
|
return <ScreenSplitPanel config={splitPanelConfig.data} />;
|
|
}
|
|
```
|
|
|
|
**권장 구현**:
|
|
|
|
```typescript
|
|
// frontend/app/(main)/screens/[screenId]/page.tsx 수정
|
|
useEffect(() => {
|
|
const loadScreen = async () => {
|
|
// 1. 분할 패널 확인
|
|
const splitPanelResult = await getScreenSplitPanel(screenId);
|
|
|
|
if (splitPanelResult.success && splitPanelResult.data) {
|
|
// 분할 패널 화면
|
|
setScreenType("split_panel");
|
|
setSplitPanelConfig(splitPanelResult.data);
|
|
return;
|
|
}
|
|
|
|
// 2. 일반 화면
|
|
const screenResult = await screenApi.getScreen(screenId);
|
|
const layoutResult = await screenApi.getScreenLayout(screenId);
|
|
|
|
setScreenType("normal");
|
|
setScreen(screenResult.data);
|
|
setLayout(layoutResult.data);
|
|
};
|
|
|
|
loadScreen();
|
|
}, [screenId]);
|
|
|
|
// 렌더링
|
|
{
|
|
screenType === "split_panel" && splitPanelConfig && (
|
|
<ScreenSplitPanel config={splitPanelConfig} />
|
|
);
|
|
}
|
|
|
|
{
|
|
screenType === "normal" && layout && (
|
|
<DynamicComponentRenderer components={layout.components} />
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 3. 컴포넌트 등록 시스템
|
|
|
|
**현재 시스템**:
|
|
|
|
```typescript
|
|
// frontend/lib/registry/components.ts
|
|
const componentRegistry = new Map<string, ComponentDefinition>();
|
|
|
|
export function registerComponent(id: string, component: any) {
|
|
componentRegistry.set(id, component);
|
|
}
|
|
```
|
|
|
|
**새로운 요구사항**:
|
|
|
|
```typescript
|
|
// DataReceivable 인터페이스 구현 필요
|
|
interface DataReceivable {
|
|
componentId: string;
|
|
componentType: ComponentType;
|
|
receiveData(data: any[], mode: DataReceiveMode): Promise<void>;
|
|
getData(): any;
|
|
clearData(): void;
|
|
}
|
|
```
|
|
|
|
**잠재적 문제**:
|
|
|
|
- ⚠️ 기존 컴포넌트들이 DataReceivable 인터페이스 미구현
|
|
- ⚠️ 데이터 수신 기능 없음
|
|
|
|
**해결 방법**:
|
|
|
|
```typescript
|
|
// Phase 5에서 구현 예정
|
|
// 기존 컴포넌트를 래핑하는 어댑터 패턴 사용
|
|
|
|
class TableComponentAdapter implements DataReceivable {
|
|
constructor(private tableComponent: any) {}
|
|
|
|
async receiveData(data: any[], mode: DataReceiveMode) {
|
|
if (mode === "append") {
|
|
this.tableComponent.addRows(data);
|
|
} else if (mode === "replace") {
|
|
this.tableComponent.setRows(data);
|
|
}
|
|
}
|
|
|
|
getData() {
|
|
return this.tableComponent.getRows();
|
|
}
|
|
|
|
clearData() {
|
|
this.tableComponent.clearRows();
|
|
}
|
|
}
|
|
```
|
|
|
|
**권장 사항**:
|
|
|
|
- ✅ 기존 컴포넌트 수정 없이 어댑터로 래핑
|
|
- ✅ 점진적으로 DataReceivable 구현
|
|
- ✅ 하위 호환성 유지
|
|
|
|
---
|
|
|
|
## 🔧 필요한 수정 사항
|
|
|
|
### 1. 화면 페이지 수정 (필수)
|
|
|
|
**파일**: `frontend/app/(main)/screens/[screenId]/page.tsx`
|
|
|
|
**수정 내용**:
|
|
|
|
```typescript
|
|
import { getScreenSplitPanel } from "@/lib/api/screenEmbedding";
|
|
import { ScreenSplitPanel } from "@/components/screen-embedding";
|
|
|
|
function ScreenViewPage() {
|
|
const [screenType, setScreenType] = useState<"normal" | "split_panel">(
|
|
"normal"
|
|
);
|
|
const [splitPanelConfig, setSplitPanelConfig] = useState<any>(null);
|
|
|
|
useEffect(() => {
|
|
const loadScreen = async () => {
|
|
// 분할 패널 확인
|
|
const splitResult = await getScreenSplitPanel(screenId);
|
|
|
|
if (splitResult.success && splitResult.data) {
|
|
setScreenType("split_panel");
|
|
setSplitPanelConfig(splitResult.data);
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
// 일반 화면 로드 (기존 로직)
|
|
// ...
|
|
};
|
|
|
|
loadScreen();
|
|
}, [screenId]);
|
|
|
|
// 렌더링
|
|
if (screenType === "split_panel" && splitPanelConfig) {
|
|
return <ScreenSplitPanel config={splitPanelConfig} />;
|
|
}
|
|
|
|
// 기존 렌더링 로직
|
|
// ...
|
|
}
|
|
```
|
|
|
|
**영향도**: 중간 (기존 로직에 조건 추가)
|
|
|
|
---
|
|
|
|
### 2. 화면 관리 UI 수정 (선택사항)
|
|
|
|
**파일**: 화면 관리 페이지
|
|
|
|
**추가 기능**:
|
|
|
|
- 화면 생성 시 "분할 패널" 타입 선택
|
|
- 분할 패널 설정 UI
|
|
- 임베딩 설정 UI
|
|
- 데이터 매핑 설정 UI
|
|
|
|
**영향도**: 낮음 (새로운 UI 추가)
|
|
|
|
---
|
|
|
|
## 📊 충돌 위험도 평가
|
|
|
|
| 항목 | 위험도 | 설명 | 조치 필요 |
|
|
| -------------------- | ------- | ------------------- | ----------------- |
|
|
| 데이터베이스 스키마 | 🟢 낮음 | 독립적인 새 테이블 | ❌ 불필요 |
|
|
| API 엔드포인트 | 🟢 낮음 | 새로운 경로 추가 | ❌ 불필요 |
|
|
| TypeScript 타입 | 🟢 낮음 | 별도 파일 | ❌ 불필요 |
|
|
| 프론트엔드 컴포넌트 | 🟢 낮음 | 별도 디렉토리 | ❌ 불필요 |
|
|
| 화면 렌더링 로직 | 🟡 중간 | 조건 분기 추가 필요 | ✅ 필요 |
|
|
| 컴포넌트 등록 시스템 | 🟡 중간 | 어댑터 패턴 필요 | ✅ 필요 (Phase 5) |
|
|
| 외래키 CASCADE | 🟡 중간 | 화면 삭제 시 주의 | ⚠️ 주의 |
|
|
|
|
**전체 위험도**: 🟢 **낮음** (대부분 독립적)
|
|
|
|
---
|
|
|
|
## ✅ 안전성 체크리스트
|
|
|
|
### 데이터베이스
|
|
|
|
- [x] 새 테이블명이 기존과 중복되지 않음
|
|
- [x] 기존 테이블 구조 변경 없음
|
|
- [x] 외래키 CASCADE 설정 완료
|
|
- [x] 멀티테넌시 (company_code) 지원
|
|
|
|
### 백엔드
|
|
|
|
- [x] 새 라우트가 기존과 충돌하지 않음
|
|
- [x] 독립적인 컨트롤러 파일
|
|
- [x] 기존 API 수정 없음
|
|
- [x] 에러 핸들링 완료
|
|
|
|
### 프론트엔드
|
|
|
|
- [x] 새 컴포넌트가 별도 디렉토리
|
|
- [x] 기존 컴포넌트 수정 없음
|
|
- [x] 독립적인 타입 정의
|
|
- [ ] 화면 페이지 수정 필요 (조건 분기)
|
|
|
|
### 호환성
|
|
|
|
- [x] 기존 화면 동작 영향 없음
|
|
- [x] 하위 호환성 유지
|
|
- [ ] 컴포넌트 어댑터 구현 (Phase 5)
|
|
|
|
---
|
|
|
|
## 🎯 권장 조치 사항
|
|
|
|
### 즉시 조치 (필수)
|
|
|
|
1. **화면 페이지 수정**
|
|
|
|
```typescript
|
|
// frontend/app/(main)/screens/[screenId]/page.tsx
|
|
// 분할 패널 확인 로직 추가
|
|
```
|
|
|
|
2. **에러 처리 강화**
|
|
```typescript
|
|
// 분할 패널 로드 실패 시 일반 화면으로 폴백
|
|
try {
|
|
const splitResult = await getScreenSplitPanel(screenId);
|
|
if (splitResult.success) {
|
|
return <ScreenSplitPanel />;
|
|
}
|
|
} catch (error) {
|
|
// 일반 화면으로 폴백
|
|
}
|
|
```
|
|
|
|
### 단계적 조치 (Phase 5-6)
|
|
|
|
1. **컴포넌트 어댑터 구현**
|
|
|
|
- TableComponent → DataReceivable
|
|
- InputComponent → DataReceivable
|
|
- 기타 컴포넌트들
|
|
|
|
2. **설정 UI 개발**
|
|
|
|
- 분할 패널 생성 UI
|
|
- 매핑 규칙 설정 UI
|
|
- 미리보기 기능
|
|
|
|
3. **테스트**
|
|
- 기존 화면 정상 동작 확인
|
|
- 분할 패널 화면 동작 확인
|
|
- 화면 전환 테스트
|
|
|
|
---
|
|
|
|
## 📝 결론
|
|
|
|
### ✅ 안전성 평가: 높음
|
|
|
|
**이유**:
|
|
|
|
1. ✅ 대부분의 코드가 독립적으로 추가됨
|
|
2. ✅ 기존 시스템 수정 최소화
|
|
3. ✅ 하위 호환성 유지
|
|
4. ✅ 외래키 CASCADE로 데이터 무결성 보장
|
|
|
|
### ⚠️ 주의 사항
|
|
|
|
1. **화면 페이지 수정 필요**
|
|
|
|
- 분할 패널 확인 로직 추가
|
|
- 조건부 렌더링 구현
|
|
|
|
2. **점진적 구현 권장**
|
|
|
|
- Phase 5: 컴포넌트 어댑터
|
|
- Phase 6: 설정 UI
|
|
- 단계별 테스트
|
|
|
|
3. **화면 삭제 시 주의**
|
|
- 임베딩 사용 여부 확인
|
|
- CASCADE로 자동 삭제됨
|
|
|
|
### 🎉 최종 결론
|
|
|
|
**충돌 위험도: 낮음 (🟢)**
|
|
|
|
새로운 시스템은 기존 시스템과 **독립적으로 동작**하며, 최소한의 수정만으로 통합 가능합니다. 화면 페이지에 조건 분기만 추가하면 바로 사용할 수 있습니다.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|