8.3 KiB
8.3 KiB
Phase 0: 데이터 마이그레이션 전략
1. 현재 데이터 구조 분석
screen_layouts.properties 구조
{
// 기본 정보
"type": "component",
"componentType": "text-input", // 기존 컴포넌트 타입
// 위치/크기
"position": { "x": 68, "y": 80, "z": 1 },
"size": { "width": 324, "height": 40 },
// 라벨 및 스타일
"label": "품목코드",
"style": {
"labelColor": "#000000",
"labelDisplay": true,
"labelFontSize": "14px",
"labelFontWeight": "500",
"labelMarginBottom": "8px"
},
// 데이터 바인딩
"tableName": "order_table",
"columnName": "part_code",
// 필드 속성
"required": true,
"readonly": false,
// 컴포넌트별 설정
"componentConfig": {
"type": "text-input",
"format": "none",
"webType": "text",
"multiline": false,
"placeholder": "텍스트를 입력하세요"
},
// 그리드 레이아웃
"gridColumns": 5,
"gridRowIndex": 0,
"gridColumnStart": 1,
"gridColumnSpan": "third",
// 기타
"parentId": null
}
2. 마이그레이션 전략: 하이브리드 방식
2.1 비파괴적 전환 (권장)
기존 필드를 유지하면서 새로운 필드를 추가하는 방식
{
// 기존 필드 유지 (하위 호환성)
"componentType": "text-input",
"componentConfig": { ... },
// 신규 필드 추가
"unifiedType": "UnifiedInput", // 새로운 통합 컴포넌트 타입
"unifiedConfig": { // 새로운 설정 구조
"type": "text",
"format": "none",
"placeholder": "텍스트를 입력하세요"
},
// 마이그레이션 메타데이터
"_migration": {
"version": "2.0",
"migratedAt": "2024-12-19T00:00:00Z",
"migratedBy": "system",
"originalType": "text-input"
}
}
2.2 렌더링 로직 수정
// 렌더러에서 unifiedType 우선 사용
function renderComponent(props: ComponentProps) {
// 신규 타입이 있으면 Unified 컴포넌트 사용
if (props.unifiedType) {
return <UnifiedComponentRenderer
type={props.unifiedType}
config={props.unifiedConfig}
/>;
}
// 없으면 기존 레거시 컴포넌트 사용
return <LegacyComponentRenderer
type={props.componentType}
config={props.componentConfig}
/>;
}
3. 컴포넌트별 매핑 규칙
3.1 text-input → UnifiedInput
// AS-IS
{
"componentType": "text-input",
"componentConfig": {
"type": "text-input",
"format": "none",
"webType": "text",
"multiline": false,
"placeholder": "텍스트를 입력하세요"
}
}
// TO-BE
{
"unifiedType": "UnifiedInput",
"unifiedConfig": {
"type": "text", // componentConfig.webType 또는 "text"
"format": "none", // componentConfig.format
"placeholder": "..." // componentConfig.placeholder
}
}
3.2 number-input → UnifiedInput
// AS-IS
{
"componentType": "number-input",
"componentConfig": {
"type": "number-input",
"webType": "number",
"min": 0,
"max": 100,
"step": 1
}
}
// TO-BE
{
"unifiedType": "UnifiedInput",
"unifiedConfig": {
"type": "number",
"min": 0,
"max": 100,
"step": 1
}
}
3.3 select-basic → UnifiedSelect
// AS-IS (code 타입)
{
"componentType": "select-basic",
"codeCategory": "ORDER_STATUS",
"componentConfig": {
"type": "select-basic",
"webType": "code",
"codeCategory": "ORDER_STATUS"
}
}
// TO-BE
{
"unifiedType": "UnifiedSelect",
"unifiedConfig": {
"mode": "dropdown",
"source": "code",
"codeGroup": "ORDER_STATUS"
}
}
// AS-IS (entity 타입)
{
"componentType": "select-basic",
"componentConfig": {
"type": "select-basic",
"webType": "entity",
"searchable": true,
"valueField": "id",
"displayField": "name"
}
}
// TO-BE
{
"unifiedType": "UnifiedSelect",
"unifiedConfig": {
"mode": "dropdown",
"source": "entity",
"searchable": true,
"valueField": "id",
"displayField": "name"
}
}
3.4 date-input → UnifiedDate
// AS-IS
{
"componentType": "date-input",
"componentConfig": {
"type": "date-input",
"webType": "date",
"format": "YYYY-MM-DD"
}
}
// TO-BE
{
"unifiedType": "UnifiedDate",
"unifiedConfig": {
"type": "date",
"format": "YYYY-MM-DD"
}
}
4. 마이그레이션 스크립트
4.1 자동 마이그레이션 함수
// lib/migration/componentMigration.ts
interface MigrationResult {
success: boolean;
unifiedType: string;
unifiedConfig: Record<string, any>;
}
export function migrateToUnified(
componentType: string,
componentConfig: Record<string, any>
): MigrationResult {
switch (componentType) {
case 'text-input':
return {
success: true,
unifiedType: 'UnifiedInput',
unifiedConfig: {
type: componentConfig.webType || 'text',
format: componentConfig.format || 'none',
placeholder: componentConfig.placeholder
}
};
case 'number-input':
return {
success: true,
unifiedType: 'UnifiedInput',
unifiedConfig: {
type: 'number',
min: componentConfig.min,
max: componentConfig.max,
step: componentConfig.step
}
};
case 'select-basic':
return {
success: true,
unifiedType: 'UnifiedSelect',
unifiedConfig: {
mode: 'dropdown',
source: componentConfig.webType || 'static',
codeGroup: componentConfig.codeCategory,
searchable: componentConfig.searchable,
valueField: componentConfig.valueField,
displayField: componentConfig.displayField
}
};
case 'date-input':
return {
success: true,
unifiedType: 'UnifiedDate',
unifiedConfig: {
type: componentConfig.webType || 'date',
format: componentConfig.format
}
};
default:
return {
success: false,
unifiedType: '',
unifiedConfig: {}
};
}
}
4.2 DB 마이그레이션 스크립트
-- 마이그레이션 백업 테이블 생성
CREATE TABLE screen_layouts_backup_v2 AS
SELECT * FROM screen_layouts;
-- 마이그레이션 실행 (text-input 예시)
UPDATE screen_layouts
SET properties = properties || jsonb_build_object(
'unifiedType', 'UnifiedInput',
'unifiedConfig', jsonb_build_object(
'type', COALESCE(properties->'componentConfig'->>'webType', 'text'),
'format', COALESCE(properties->'componentConfig'->>'format', 'none'),
'placeholder', properties->'componentConfig'->>'placeholder'
),
'_migration', jsonb_build_object(
'version', '2.0',
'migratedAt', NOW(),
'originalType', 'text-input'
)
)
WHERE properties->>'componentType' = 'text-input';
5. 롤백 전략
5.1 롤백 스크립트
-- 마이그레이션 전 상태로 복원
UPDATE screen_layouts sl
SET properties = slb.properties
FROM screen_layouts_backup_v2 slb
WHERE sl.layout_id = slb.layout_id;
-- 또는 신규 필드만 제거
UPDATE screen_layouts
SET properties = properties - 'unifiedType' - 'unifiedConfig' - '_migration';
5.2 단계적 롤백
// 특정 화면만 롤백
async function rollbackScreen(screenId: number) {
await db.query(`
UPDATE screen_layouts sl
SET properties = properties - 'unifiedType' - 'unifiedConfig' - '_migration'
WHERE screen_id = $1
`, [screenId]);
}
6. 마이그레이션 일정
| 단계 | 작업 | 대상 | 시점 |
|---|---|---|---|
| 1 | 백업 테이블 생성 | 전체 | Phase 1 시작 전 |
| 2 | UnifiedInput 마이그레이션 | text-input, number-input | Phase 1 중 |
| 3 | UnifiedSelect 마이그레이션 | select-basic | Phase 1 중 |
| 4 | UnifiedDate 마이그레이션 | date-input | Phase 1 중 |
| 5 | 검증 및 테스트 | 전체 | Phase 1 완료 후 |
| 6 | 레거시 필드 제거 | 전체 | Phase 5 (추후) |
7. 주의사항
- 항상 백업 먼저: 마이그레이션 전 반드시 백업 테이블 생성
- 점진적 전환: 한 번에 모든 컴포넌트를 마이그레이션하지 않음
- 하위 호환성: 기존 필드 유지로 롤백 가능하게
- 테스트 필수: 각 마이그레이션 단계별 화면 테스트