ERP-node/테이블_복제_기능_구현_계획서.md

880 lines
26 KiB
Markdown
Raw Normal View History

feat: 테이블 복제 기능 구현 (최고 관리자 전용) ✨ 새로운 기능 - 테이블 타입 관리에 테이블 복제 기능 추가 - 기존 테이블의 설정과 컬럼 정보를 복사하여 새 테이블 생성 - 최고 관리자만 사용 가능 (company_code = '*' AND userType = 'SUPER_ADMIN') - 테이블 1개 선택 시에만 복제 버튼 활성화 🎨 UI 개선 - 테이블 목록에 '테이블 복제' 버튼 추가 (Copy 아이콘) - CreateTableModal을 복제 모드로 재사용 - 복제 모드 시 제목/설명/버튼 텍스트 동적 변경 - 원본 테이블 정보 자동 로드 🔧 기술적 개선 - CreateTableModal에 mode/sourceTableName props 추가 - 복제 모드 감지 및 데이터 자동 로드 로직 구현 - API 타입 정의 수정 (ColumnListData 인터페이스 추가) - 백엔드 응답 구조와 프론트엔드 타입 일치화 🐛 버그 수정 - API 응답 구조 불일치 문제 해결 - ColumnListResponse 타입 수정 (배열 → 객체) - 데이터 파싱 로직 수정 (data.columns 접근) - 디버그 로그 추가로 문제 추적 개선 📝 변경된 파일 - frontend/app/(main)/admin/tableMng/page.tsx - frontend/components/admin/CreateTableModal.tsx - frontend/lib/api/tableManagement.ts - frontend/types/ddl.ts - 테이블_복제_기능_구현_계획서.md (신규) ✅ 테스트 완료 - 최고 관리자 권한 체크 - 테이블 정보 로드 - 컬럼 정보 복제 - 새 테이블명 입력 및 검증 - 테이블 생성 및 목록 갱신
2025-10-31 17:58:49 +09:00
# 테이블 복제 기능 구현 계획서
## 📋 개요
테이블 타입 관리 시스템에 기존 테이블을 복제하여 새로운 테이블을 생성하는 기능을 추가합니다. 복제 시 원본 테이블의 컬럼 구조와 설정을 가져와 사용자가 수정 후 저장할 수 있도록 합니다.
---
## 🎯 주요 요구사항
### 1. 권한 제한
- **최고 관리자(company_code = "\*" && user_type = "SUPER_ADMIN")만 사용 가능**
- 일반 회사 사용자는 테이블 복제 버튼이 보이지 않음
### 2. 복제 프로세스
1. 테이블 목록에서 복제할 테이블 선택 (체크박스)
2. "복제" 버튼 클릭
3. 기존 "테이블 생성" 모달이 "테이블 복제" 모드로 열림
4. 원본 테이블의 설정이 자동으로 채워짐:
- 테이블명: 빈 상태 (사용자가 새로 입력)
- 테이블 설명: 원본 설명 복사 (수정 가능)
- 컬럼 정의: 원본 컬럼들이 모두 로드됨 (수정/삭제 가능)
5. 사용자가 수정 후 저장
6. 테이블명 중복 검증 (실시간)
7. DDL 실행 및 테이블 생성
### 3. 제약사항
- **테이블명 중복 불가** (실시간 검증)
- 시스템 테이블은 복제 불가 (선택 불가 처리)
- 최소 1개 이상의 컬럼 필요
---
## 🏗️ 구현 아키텍처
### 컴포넌트 구조
```
📦 테이블 타입 관리 페이지 (tableMng/page.tsx)
├── 📄 테이블 목록 (기존)
│ ├── 체크박스 (단일 선택 또는 다중 선택)
│ └── 복제 버튼 (최고 관리자만 표시)
└── 🔧 CreateTableModal 확장 (CreateTableModal.tsx)
├── 모드: "create" | "duplicate"
├── Props 추가: duplicateTableName?, isDuplicateMode?
├── 복제 모드 시 자동 로드:
│ ├── 원본 테이블 설명
│ └── 원본 컬럼 목록 (전체 설정 포함)
└── 실시간 테이블명 중복 검증
```
---
## 📝 상세 구현 계획
### Phase 1: UI 추가 (테이블 목록 페이지)
#### 1.1. 체크박스 추가
**파일**: `frontend/app/(main)/admin/tableMng/page.tsx`
**변경사항**:
- 각 테이블 행에 체크박스 추가
- 선택된 테이블 상태 관리: `selectedTableIds` (Set<string>)
- 시스템 테이블은 체크박스 비활성화
```typescript
// 상태 추가
const [selectedTableIds, setSelectedTableIds] = useState<Set<string>>(
new Set()
);
// 최고 관리자 여부 확인 수정 (기존 코드 수정 필요)
// ❌ 기존: const isSuperAdmin = user?.companyCode === "*";
// ✅ 수정: const isSuperAdmin = user?.companyCode === "*" && user?.userType === "SUPER_ADMIN";
// 시스템 테이블 목록 (복제 불가)
const SYSTEM_TABLES = [
"user_info",
"company_info",
"menu_info",
"auth_group",
"menu_auth_group",
"user_auth",
"dept_info",
"code_info",
"code_category",
// ... 기타 시스템 테이블
];
// 체크박스 핸들러
const handleTableSelect = (tableName: string) => {
setSelectedTableIds((prev) => {
const newSet = new Set(prev);
if (newSet.has(tableName)) {
newSet.delete(tableName);
} else {
newSet.add(tableName);
}
return newSet;
});
};
```
#### 1.2. 복제 버튼 추가
**위치**: 테이블 목록 상단 (새 테이블 생성 버튼 옆)
```typescript
{
isSuperAdmin && (
<Button
variant="outline"
onClick={handleDuplicateTable}
disabled={selectedTableIds.size !== 1}
className="h-9 text-xs sm:h-10 sm:text-sm"
>
<Copy className="mr-2 h-4 w-4" />
테이블 복제
</Button>
);
}
```
**조건**:
- 최고 관리자만 표시
- 정확히 1개의 테이블이 선택된 경우만 활성화
- 선택된 테이블이 시스템 테이블인 경우 비활성화
---
### Phase 2: CreateTableModal 확장
#### 2.1. Props 확장
**파일**: `frontend/components/admin/CreateTableModal.tsx`
```typescript
export interface CreateTableModalProps {
isOpen: boolean;
onClose: () => void;
onSuccess: (result: any) => void;
// 🆕 복제 모드 관련
mode?: "create" | "duplicate";
sourceTableName?: string; // 복제 대상 테이블명
}
```
#### 2.2. 복제 모드 감지 및 데이터 로드
```typescript
export function CreateTableModal({
isOpen,
onClose,
onSuccess,
mode = "create",
sourceTableName,
}: CreateTableModalProps) {
const isDuplicateMode = mode === "duplicate" && sourceTableName;
// 복제 모드일 때 원본 테이블 정보 로드
useEffect(() => {
if (isOpen && isDuplicateMode && sourceTableName) {
loadSourceTableData(sourceTableName);
}
}, [isOpen, isDuplicateMode, sourceTableName]);
const loadSourceTableData = async (tableName: string) => {
setLoading(true);
try {
// 1. 테이블 기본 정보 조회
const tableResponse = await apiClient.get(
`/table-management/tables/${tableName}`
);
if (tableResponse.data.success) {
const tableInfo = tableResponse.data.data;
setDescription(tableInfo.description || "");
}
// 2. 컬럼 정보 조회
const columnsResponse = await tableManagementApi.getColumnList(tableName);
if (columnsResponse.success && columnsResponse.data) {
const loadedColumns: CreateColumnDefinition[] =
columnsResponse.data.map((col, idx) => ({
name: col.columnName,
label: col.displayName || col.columnName,
inputType: col.webType as any,
nullable: col.isNullable === "YES",
order: idx + 1,
description: col.description,
codeCategory: col.codeCategory || undefined,
referenceTable: col.referenceTable || undefined,
referenceColumn: col.referenceColumn || undefined,
displayColumn: col.displayColumn || undefined,
}));
setColumns(loadedColumns);
toast.success(`${tableName} 테이블 정보를 불러왔습니다.`);
}
} catch (error: any) {
console.error("원본 테이블 정보 로드 실패:", error);
toast.error("원본 테이블 정보를 불러오는데 실패했습니다.");
onClose();
} finally {
setLoading(false);
}
};
}
```
#### 2.3. 모달 제목 및 버튼 텍스트 변경
```typescript
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="max-w-[95vw] sm:max-w-[800px]">
<DialogHeader>
<DialogTitle className="text-base sm:text-lg">
{isDuplicateMode ? "테이블 복제" : "새 테이블 생성"}
</DialogTitle>
<DialogDescription className="text-xs sm:text-sm">
{isDuplicateMode
? `${sourceTableName} 테이블을 복제하여 새 테이블을 생성합니다.`
: "데이터베이스에 새로운 테이블을 생성합니다."}
</DialogDescription>
</DialogHeader>
{/* ... 폼 내용 ... */}
<DialogFooter className="gap-2 sm:gap-0">
<Button
variant="outline"
onClick={onClose}
className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
>
취소
</Button>
<Button
onClick={handleCreateTable}
disabled={!isFormValid || loading}
className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
>
{loading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
생성 중...
</>
) : isDuplicateMode ? (
"복제 생성"
) : (
"생성"
)}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
```
#### 2.4. 테이블명 실시간 중복 검증 강화
```typescript
// 테이블명 변경 시 실시간 검증
const handleTableNameChange = async (name: string) => {
setTableName(name);
// 기본 검증
const error = validateTableName(name);
if (error) {
setTableNameError(error);
return;
}
// 중복 검증
setValidating(true);
try {
const result = await tableManagementApi.checkTableExists(name);
if (result.success && result.data?.exists) {
setTableNameError("이미 존재하는 테이블명입니다.");
} else {
setTableNameError("");
}
} catch (error) {
console.error("테이블명 검증 오류:", error);
} finally {
setValidating(false);
}
};
```
---
### Phase 3: 백엔드 API 구현 (필요 시)
#### 3.1. 테이블 기본 정보 조회 API
**엔드포인트**: `GET /api/table-management/tables/:tableName`
**응답**:
```json
{
"success": true,
"data": {
"tableName": "contracts",
"displayName": "계약 관리",
"description": "계약 정보를 관리하는 테이블",
"columnCount": 15
}
}
```
**구현 위치**: `backend-node/src/controllers/tableManagementController.ts`
```typescript
/**
* 특정 테이블의 기본 정보 조회
*/
async getTableInfo(req: Request, res: Response) {
const { tableName } = req.params;
try {
const query = `
SELECT
t.table_name as "tableName",
dt.display_name as "displayName",
dt.description as "description",
COUNT(c.column_name) as "columnCount"
FROM information_schema.tables t
LEFT JOIN db_table_types dt ON dt.table_name = t.table_name
LEFT JOIN information_schema.columns c ON c.table_name = t.table_name
WHERE t.table_schema = 'public'
AND t.table_name = $1
GROUP BY t.table_name, dt.display_name, dt.description
`;
const result = await pool.query(query, [tableName]);
if (result.rows.length === 0) {
return res.status(404).json({
success: false,
message: `테이블 '${tableName}'을 찾을 수 없습니다.`
});
}
return res.json({
success: true,
data: result.rows[0]
});
} catch (error: any) {
logger.error("테이블 정보 조회 실패", { tableName, error: error.message });
return res.status(500).json({
success: false,
message: "테이블 정보를 조회하는데 실패했습니다."
});
}
}
```
#### 3.2. 라우트 추가
**파일**: `backend-node/src/routes/tableManagement.ts`
```typescript
// 테이블 기본 정보 조회 (새로 추가)
router.get("/tables/:tableName", controller.getTableInfo);
```
---
### Phase 4: 통합 및 테스트
#### 4.1. 테이블 목록 페이지에서 복제 흐름 구현
```typescript
// tableMng/page.tsx
const handleDuplicateTable = async () => {
// 선택된 테이블 1개 확인
if (selectedTableIds.size !== 1) {
toast.error("복제할 테이블을 1개만 선택해주세요.");
return;
}
const sourceTable = Array.from(selectedTableIds)[0];
// 시스템 테이블 체크
if (SYSTEM_TABLES.includes(sourceTable)) {
toast.error("시스템 테이블은 복제할 수 없습니다.");
return;
}
// 복제 모달 열기
setDuplicateSourceTable(sourceTable);
setCreateTableModalMode("duplicate");
setCreateTableModalOpen(true);
};
// 모달 컴포넌트
<CreateTableModal
isOpen={createTableModalOpen}
onClose={() => {
setCreateTableModalOpen(false);
setDuplicateSourceTable(null);
setCreateTableModalMode("create");
}}
onSuccess={(result) => {
loadTables(); // 테이블 목록 새로고침
setSelectedTableIds(new Set()); // 선택 초기화
}}
mode={createTableModalMode}
sourceTableName={duplicateSourceTable}
/>;
```
---
## 🧪 테스트 시나리오
### 테스트 케이스 1: 정상 복제 흐름
1. 최고 관리자로 로그인
2. 테이블 타입 관리 페이지 접속
3. 복제할 테이블 1개 선택 (예: `contracts`)
4. "테이블 복제" 버튼 클릭
5. 모달이 열리고 원본 테이블 정보가 자동으로 로드됨
6. 새 테이블명 입력 (예: `contracts_backup`)
7. 컬럼 정보 확인 및 필요 시 수정
8. "복제 생성" 버튼 클릭
9. 테이블이 생성되고 목록에 표시됨
10. 성공 토스트 메시지 확인
### 테스트 케이스 2: 테이블명 중복 검증
1. 테이블 복제 흐름 시작
2. 기존에 존재하는 테이블명 입력 (예: `user_info`)
3. 실시간으로 에러 메시지 표시: "이미 존재하는 테이블명입니다."
4. "복제 생성" 버튼 비활성화 확인
### 테스트 케이스 3: 시스템 테이블 복제 제한
1. 시스템 테이블 선택 시도 (예: `user_info`)
2. 체크박스가 비활성화되어 있음
3. 또는 선택 시 경고 메시지: "시스템 테이블은 복제할 수 없습니다."
### 테스트 케이스 4: 권한 제한
1. 일반 회사 사용자로 로그인
2. 테이블 타입 관리 페이지 접속
3. "테이블 복제" 버튼이 보이지 않음
4. 체크박스도 표시되지 않음 (선택 사항)
### 테스트 케이스 5: 컬럼 수정 후 복제
1. 테이블 복제 모달 열기
2. 원본 컬럼 중 일부 삭제
3. 새 컬럼 추가
4. 컬럼 라벨 변경
5. 저장 시 수정된 구조로 테이블 생성됨
---
## 📊 데이터 흐름도
```
┌─────────────────────────────────────────────────────────────┐
│ 1. 테이블 선택 (체크박스) │
│ - 사용자가 복제할 테이블 1개 선택 │
└────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. "테이블 복제" 버튼 클릭 │
│ - handleDuplicateTable() 실행 │
└────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 3. CreateTableModal 열림 (복제 모드) │
│ - mode: "duplicate" │
│ - sourceTableName: 선택된 테이블명 │
└────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 4. 원본 테이블 정보 자동 로드 │
│ - GET /api/table-management/tables/:tableName │
│ → 테이블 설명 가져오기 │
│ - GET /api/table-management/tables/:tableName/columns │
│ → 모든 컬럼 정보 가져오기 │
└────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 5. 사용자 편집 │
│ - 새 테이블명 입력 (실시간 중복 검증) │
│ - 테이블 설명 수정 │
│ - 컬럼 추가/삭제/수정 │
└────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 6. "복제 생성" 버튼 클릭 │
│ - POST /api/ddl/create-table │
│ (기존 테이블 생성 API 재사용) │
└────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 7. DDL 실행 및 테이블 생성 │
│ - CREATE TABLE ... (새 테이블명) │
│ - db_column_types 레코드 생성 │
│ - db_table_types 레코드 생성 │
└────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 8. 성공 처리 │
│ - 모달 닫기 │
│ - 테이블 목록 새로고침 │
│ - 성공 토스트 메시지 표시 │
└─────────────────────────────────────────────────────────────┘
```
---
## 🚀 구현 순서 (우선순위)
### Step 0: 기존 코드 수정 (최고 관리자 체크 로직)
1.`frontend/app/(main)/admin/tableMng/page.tsx` - line 101
- 기존: `const isSuperAdmin = user?.companyCode === "*";`
- 수정: `const isSuperAdmin = user?.companyCode === "*" && user?.userType === "SUPER_ADMIN";`
**예상 소요 시간**: 5분
### Step 1: UI 기반 구조 (프론트엔드)
1. ✅ 테이블 목록에 체크박스 추가
2. ✅ 복제 버튼 추가 (최고 관리자만 표시)
3. ✅ 선택된 테이블 상태 관리
4. ✅ 시스템 테이블 복제 제한
**예상 소요 시간**: 1-2시간
### Step 2: CreateTableModal 확장
1. ✅ Props 확장 (mode, sourceTableName)
2. ✅ 복제 모드 감지 로직
3. ✅ 원본 테이블 정보 로드 함수
4. ✅ 모달 제목 및 버튼 텍스트 동적 변경
5. ✅ 테이블명 실시간 중복 검증
**예상 소요 시간**: 2-3시간
### Step 3: 백엔드 API 추가 (필요 시)
1. ✅ 테이블 기본 정보 조회 API 추가
2. ✅ 라우트 추가
3. ✅ 권한 검증 (최고 관리자만)
**예상 소요 시간**: 1시간
### Step 4: 통합 및 테스트
1. ✅ 전체 흐름 통합
2. ✅ 각 테스트 케이스 실행
3. ✅ 버그 수정 및 최적화
**예상 소요 시간**: 2-3시간
**전체 예상 소요 시간**: **6-9시간** (기존 코드 수정 포함)
---
## 🔒 보안 고려사항
### 1. 권한 체크
- 프론트엔드: `isSuperAdmin` 플래그로 UI 제어
- `user?.companyCode === "*" && user?.userType === "SUPER_ADMIN"`
- 백엔드: 모든 API 호출 시 두 가지 조건 모두 검증
```typescript
// 백엔드 미들웨어 (기존 requireSuperAdmin 재사용)
import { requireSuperAdmin } from "@/middleware/superAdminMiddleware";
// 또는 인라인 체크
const requireSuperAdmin = (req: Request, res: Response, next: NextFunction) => {
if (req.user?.companyCode !== "*" || req.user?.userType !== "SUPER_ADMIN") {
return res.status(403).json({
success: false,
message: "최고 관리자만 접근할 수 있습니다.",
});
}
next();
};
// 라우트 적용
router.post(
"/tables/:tableName/duplicate",
requireSuperAdmin,
controller.duplicateTable
);
```
### 2. 시스템 테이블 보호
- SYSTEM_TABLES 상수로 관리
- 복제 시도 시 서버 측에서도 검증
### 3. 테이블명 검증
- SQL Injection 방지: 정규식 검증 (`^[a-z][a-z0-9_]*$`)
- 예약어 체크
- 길이 제한 (3-63자)
---
## 📌 주요 체크포인트
### 사용자 경험 (UX)
- ✅ 복제 버튼은 1개 테이블 선택 시에만 활성화
- ✅ 로딩 상태 표시 (모달 열릴 때)
- ✅ 실시간 테이블명 중복 검증
- ✅ 명확한 에러 메시지
- ✅ 성공/실패 토스트 알림
### 데이터 일관성
- ✅ 원본 테이블의 모든 컬럼 정보 정확히 복사
- ✅ webType, codeCategory, referenceTable 등 관계 정보 포함
- ✅ 컬럼 순서 유지
### 성능
- ✅ 컬럼이 많은 테이블도 빠르게 로드
- ✅ 불필요한 API 호출 최소화
- ✅ 로딩 중 중복 요청 방지
---
## 🎨 UI 스타일 가이드
### 복제 버튼
```tsx
<Button
variant="outline"
onClick={handleDuplicateTable}
disabled={selectedTableIds.size !== 1}
className="h-9 text-xs sm:h-10 sm:text-sm"
>
<Copy className="mr-2 h-4 w-4" />
테이블 복제
</Button>
```
### 체크박스 (테이블 행)
```tsx
<Checkbox
checked={selectedTableIds.has(table.tableName)}
onCheckedChange={() => handleTableSelect(table.tableName)}
disabled={SYSTEM_TABLES.includes(table.tableName)}
className="h-4 w-4"
/>
```
### 모달 제목 (복제 모드)
```tsx
<DialogTitle className="text-base sm:text-lg">
테이블 복제
</DialogTitle>
<DialogDescription className="text-xs sm:text-sm">
{sourceTableName} 테이블을 복제하여 새 테이블을 생성합니다.
</DialogDescription>
```
---
## 📚 참고 자료
### 기존 구현 파일
- `frontend/app/(main)/admin/tableMng/page.tsx` - 테이블 타입 관리 페이지
- `frontend/components/admin/CreateTableModal.tsx` - 테이블 생성 모달
- `frontend/components/admin/ColumnDefinitionTable.tsx` - 컬럼 정의 테이블
- `frontend/lib/api/tableManagement.ts` - 테이블 관리 API
- `backend-node/src/controllers/tableManagementController.ts` - 백엔드 컨트롤러
### 유사 기능 참고
- Yard 레이아웃 복제 기능: `YardLayoutList.tsx` (onDuplicate 핸들러)
- 메뉴 복제 기능 (있다면)
---
## ✅ 완료 기준
### Phase 1 완료 조건
- [ ] 테이블 목록에 체크박스 추가됨
- [ ] 복제 버튼 추가 (최고 관리자만 보임)
- [ ] 1개 테이블 선택 시에만 버튼 활성화
- [ ] 시스템 테이블은 선택 불가
### Phase 2 완료 조건
- [ ] CreateTableModal이 복제 모드를 지원
- [ ] 원본 테이블 정보가 자동으로 로드됨
- [ ] 모달 제목과 버튼 텍스트가 모드에 따라 변경됨
- [ ] 테이블명 실시간 중복 검증 작동
### Phase 3 완료 조건
- [ ] 테이블 기본 정보 조회 API 추가
- [ ] 최고 관리자 권한 검증
- [ ] 라우트 등록
### Phase 4 완료 조건
- [ ] 모든 테스트 케이스 통과
- [ ] 버그 수정 완료
- [ ] 사용자 가이드 문서 작성
---
## 🐛 예상 이슈 및 해결 방안
### 이슈 1: 컬럼이 너무 많은 테이블 복제 시 느림
**해결방안**:
- 페이지네이션 없이 전체 컬럼 한 번에 로드 (`size=9999`)
- 로딩 스피너 표시
- 필요시 API에서 캐싱 적용
### 이슈 2: 복제 후 테이블명 입력 잊음
**해결방안**:
- 테이블명 필드를 비워두고 포커스 자동 이동
- placeholder에 예시 제공 (예: `contracts_copy`)
### 이슈 3: 시스템 테이블 실수로 복제
**해결방안**:
- 프론트엔드와 백엔드 양쪽에서 검증
- SYSTEM_TABLES 리스트 관리
### 이슈 4: 참조 무결성 (Foreign Key) 복사 여부
**해결방안**:
- 현재는 컬럼 구조만 복사
- Foreign Key는 사용자가 수동으로 설정
- 향후 고급 기능으로 제약조건 복사 추가 가능
---
## 🔮 향후 개선 방향 (Optional)
### 1. 다중 테이블 복제
- 여러 테이블을 한 번에 복제
- 접두사 또는 접미사 자동 추가 (예: `_copy`)
### 2. 데이터 포함 복제
- 테이블 구조 + 데이터도 함께 복제
- `INSERT INTO ... SELECT` 방식
### 3. 제약조건 복사
- Primary Key, Foreign Key, Unique, Check 등
- 인덱스도 함께 복사
### 4. 복제 템플릿
- 자주 사용하는 테이블 구조를 템플릿으로 저장
- 빠른 복제 기능
### 5. 복제 이력 관리
- 어떤 테이블이 어디서 복제되었는지 추적
- DDL 로그에 복제 정보 기록
---
## 📞 문의 및 지원
- 기술 문의: 개발팀
- 사용 방법: 사용자 가이드 참조
- 버그 제보: 이슈 트래커
---
**작성일**: 2025-10-31
**작성자**: AI Assistant
**버전**: 1.0
**상태**: ✅ 구현 완료
---
## 🐛 버그 수정 내역
### API 응답 구조 불일치 문제
**문제**: 모달이 뜨자마자 바로 닫히면서 "테이블 정보를 불러올 수 없습니다" 토스트 표시
**원인**:
- 백엔드 API는 `{ columns: [], total, page, size }` 형태로 반환
- 프론트엔드는 `data`를 직접 배열로 취급
- 타입 정의: `ColumnListResponse extends ApiResponse<ColumnTypeInfo[]>` (잘못됨)
**해결**:
1. **API 타입 수정** (`frontend/lib/api/tableManagement.ts`)
```typescript
// 추가된 타입
export interface ColumnListData {
columns: ColumnTypeInfo[];
total: number;
page: number;
size: number;
totalPages: number;
}
// 수정된 타입
export interface ColumnListResponse extends ApiResponse<ColumnListData> {}
```
2. **CreateTableModal 데이터 파싱 수정**
```typescript
// Before (잘못됨)
if (
columnsResponse.success &&
columnsResponse.data &&
columnsResponse.data.length > 0
) {
const firstColumn = columnsResponse.data[0];
}
// After (올바름)
if (columnsResponse.success && columnsResponse.data) {
const columnsList = columnsResponse.data.columns;
if (columnsList && columnsList.length > 0) {
const firstColumn = columnsList[0];
}
}
```
3. **디버그 로그 추가**
- API 응답 전체 로그
- 컬럼 리스트 추출 후 로그
- 에러 상황별 상세 로그
**결과**:
- ✅ 복제 모드에서 테이블 정보 정상 로드
- ✅ 타입 안전성 향상
- ✅ 에러 핸들링 개선