ERP-node/화면_임베딩_시스템_충돌_분석_보고서.md

515 lines
12 KiB
Markdown
Raw Permalink Normal View History

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