# 본서버 → 개발서버 마이그레이션 가이드 (공용)

> **이 문서는 다음 AI 에이전트가 마이그레이션 작업을 이어받을 때 참고하는 핵심 가이드입니다.**

---

## 빠른 시작

### 마이그레이션 방향 (절대 잊지 말 것)

```
본서버 (Production)  →  개발서버 (Development)
211.115.91.141:11134     39.117.244.52:11132
screen_layouts (V1)      screen_layouts_v2 (V2)
```

**반대로 하면 안 됨!** 개발서버 완성 후 → 본서버로 배포 예정

### DB 접속 정보

```bash
# 본서버 (Production)
docker exec pms-backend-mac node -e '
const { Pool } = require("pg");
const pool = new Pool({ 
  connectionString: "postgresql://postgres:vexplor0909!!@211.115.91.141:11134/plm?sslmode=disable", 
  ssl: false 
});
// 쿼리 실행
'

# 개발서버 (Development)
docker exec pms-backend-mac node -e '
const { Pool } = require("pg");
const pool = new Pool({ 
  connectionString: "postgresql://postgres:ph0909!!@39.117.244.52:11132/plm?sslmode=disable", 
  ssl: false 
});
// 쿼리 실행
'
```

---

## 절대 주의: 컴포넌트-컬럼 연결 (이전 실패 원인)

### "component" vs "v2-input" 구분

```
❌ 잘못된 상태                    ✅ 올바른 상태
┌──────────────────┐              ┌──────────────────┐
│ component        │              │ v2-input         │
│ 업체코드         │              │ 업체코드         │
└──────────────────┘              └──────────────────┘
    ↑                                 ↑
    overrides.type 없음               overrides.type = "v2-input"
```

**핵심 원인**: 컴포넌트를 그냥 배치하면 "component"로 표시됨. 반드시 왼쪽 패널에서 테이블 컬럼을 **드래그**해야 올바른 v2-xxx 컴포넌트가 생성됨.

### 🔥 핵심 발견: overrides.type 필수 (2026-02-04 발견)

**"component"로 표시되는 근본 원인:**

| 항목 | 드래그로 배치 | 마이그레이션 (잘못된) |
|------|---------------|----------------------|
| `overrides.type` | **"v2-input"** ✅ | **없음** ❌ |
| `overrides.webType` | "text" 등 | 없음 |
| `overrides.tableName` | "carrier_mng" 등 | 없음 |

**프론트엔드가 컴포넌트 타입을 인식하는 방법:**
1. `overrides.type` 확인 → 있으면 해당 값 사용 (예: "v2-input")
2. 없으면 → 기본값 "component"로 폴백

**결론**: 마이그레이션 시 `overrides.type` 필드를 반드시 설정해야 함!

### input_type → V2 컴포넌트 자동 매핑

| table_type_columns.input_type | 드래그 시 생성되는 V2 컴포넌트 |
|-------------------------------|-------------------------------|
| text | v2-input |
| number | v2-input (type=number) |
| date | v2-date |
| category | v2-select (category_values 연동) |
| numbering | v2-numbering-rule 또는 v2-input |
| entity | v2-entity-search |

**절대 규칙**: 컴포넌트가 "component"로 표시되면 연결 실패 상태. 반드시 "v2-xxx"로 표시되어야 함.

---

## 핵심 개념

### V1 vs V2 구조 차이

| 구분 | V1 (본서버) | V2 (개발서버) |
|------|-------------|---------------|
| 테이블 | screen_layouts | screen_layouts_v2 |
| 레코드 | 컴포넌트별 1개 | 화면당 1개 |
| 설정 저장 | properties JSONB | layout_data.components[].overrides |
| 채번/카테고리 | menu_objid 기반 | table_name + column_name 기반 |
| 컴포넌트 참조 | component_type 문자열 | url 경로 (@/lib/registry/...) |

### 데이터 타입 관리 (V2)

```
table_type_columns (input_type)
├── 'category' → category_values 테이블
├── 'numbering' → numbering_rules 테이블 (detail_settings.numberingRuleId)
├── 'entity' → 엔티티 검색
└── 'text', 'number', 'date', etc.
```

### 컴포넌트 URL 매핑

```typescript
const V1_TO_V2_MAPPING = {
  'table-list': '@/lib/registry/components/v2-table-list',
  'button-primary': '@/lib/registry/components/v2-button-primary',
  'text-input': '@/lib/registry/components/v2-text-input',
  'select-basic': '@/lib/registry/components/v2-select',
  'date-input': '@/lib/registry/components/v2-date-input',
  'entity-search-input': '@/lib/registry/components/v2-entity-search',
  'category-manager': '@/lib/registry/components/v2-category-manager',
  'numbering-rule': '@/lib/registry/components/v2-numbering-rule',
  'tabs-widget': '@/lib/registry/components/v2-tabs-widget',
  'textarea-basic': '@/lib/registry/components/v2-textarea',
};
```

### 모달 처리 방식 변경

- **V1**: 별도 화면(screen_id)으로 모달 관리
- **V2**: 부모 화면에 overlay/dialog 컴포넌트로 통합

---

## 마이그레이션 대상 메뉴 현황

### 품질관리 (우선순위 1)

| 본서버 코드 | 화면명 | 상태 | 비고 |
|-------------|--------|------|------|
| COMPANY_7_126 | 검사정보 관리 | ✅ V2 존재 | 컴포넌트 검증 필요 |
| COMPANY_7_127 | 품목옵션 설정 | ✅ V2 존재 | v2-category-manager 사용중 |
| COMPANY_7_138 | 카테고리 설정 | ❌ 누락 | table_name 기반으로 변경 |
| COMPANY_7_139 | 코드 설정 | ❌ 누락 | table_name 기반으로 변경 |
| COMPANY_7_142 | 검사장비 관리 | ❌ 누락 | 모달 통합 필요 |
| COMPANY_7_143 | 검사장비 등록모달 | ❌ 누락 | → 142에 통합 |
| COMPANY_7_144 | 불량기준 정보 | ❌ 누락 | 모달 통합 필요 |
| COMPANY_7_145 | 불량기준 등록모달 | ❌ 누락 | → 144에 통합 |

### 다음 마이그레이션 대상 (미정)

- [ ] 물류관리
- [ ] 생산관리
- [ ] 영업관리
- [ ] 기타 메뉴들

---

## 마이그레이션 작업 절차

### Step 1: 분석

```sql
-- 본서버 특정 메뉴 화면 목록 조회
SELECT 
  sd.screen_id, sd.screen_code, sd.screen_name, sd.table_name,
  COUNT(sl.layout_id) as component_count
FROM screen_definitions sd
LEFT JOIN screen_layouts sl ON sd.screen_id = sl.screen_id
WHERE sd.screen_name LIKE '%[메뉴명]%'
  AND sd.company_code = 'COMPANY_7'
GROUP BY sd.screen_id, sd.screen_code, sd.screen_name, sd.table_name;

-- 개발서버 V2 현황 확인
SELECT 
  sd.screen_id, sd.screen_code, sd.screen_name,
  sv2.layout_id IS NOT NULL as has_v2
FROM screen_definitions sd
LEFT JOIN screen_layouts_v2 sv2 ON sd.screen_id = sv2.screen_id
WHERE sd.company_code = 'COMPANY_7';
```

### Step 2: screen_definitions 동기화

본서버에만 있는 화면을 개발서버에 추가

### Step 3: V1 → V2 레이아웃 변환

```typescript
// layout_data 구조
{
  "version": "2.0",
  "components": [
    {
      "id": "comp_xxx",
      "url": "@/lib/registry/components/v2-table-list",
      "position": { "x": 0, "y": 0 },
      "size": { "width": 100, "height": 50 },
      "displayOrder": 0,
      "overrides": {
        "tableName": "테이블명",
        "columns": ["컬럼1", "컬럼2"]
      }
    }
  ]
}
```

### Step 4: 카테고리 데이터 확인/생성

```sql
-- 테이블의 category 컬럼 확인
SELECT column_name, column_label
FROM table_type_columns
WHERE table_name = '[테이블명]'
  AND input_type = 'category';

-- category_values 데이터 확인
SELECT value_id, value_code, value_label
FROM category_values
WHERE table_name = '[테이블명]'
  AND column_name = '[컬럼명]'
  AND company_code = 'COMPANY_7';
```

### Step 5: 채번 규칙 확인/생성

```sql
-- numbering 컬럼 확인
SELECT column_name, column_label, detail_settings
FROM table_type_columns
WHERE table_name = '[테이블명]'
  AND input_type = 'numbering';

-- numbering_rules 데이터 확인
SELECT rule_id, rule_name, table_name, column_name
FROM numbering_rules
WHERE company_code = 'COMPANY_7';
```

### Step 6: 검증

- [ ] 화면 렌더링 확인
- [ ] 컴포넌트 동작 확인
- [ ] 저장/수정/삭제 테스트
- [ ] 카테고리 드롭다운 동작
- [ ] 채번 규칙 동작

---

## 핵심 테이블 스키마

### screen_layouts_v2

```sql
CREATE TABLE screen_layouts_v2 (
  layout_id SERIAL PRIMARY KEY,
  screen_id INTEGER NOT NULL,
  company_code VARCHAR(20) NOT NULL,
  layout_data JSONB NOT NULL DEFAULT '{}'::jsonb,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(screen_id, company_code)
);
```

### category_values

```sql
-- 핵심 컬럼
value_id, table_name, column_name, value_code, value_label,
parent_value_id, depth, path, company_code
```

### numbering_rules + numbering_rule_parts

```sql
-- numbering_rules 핵심 컬럼
rule_id, rule_name, table_name, column_name, separator,
reset_period, current_sequence, company_code

-- numbering_rule_parts 핵심 컬럼
rule_id, part_order, part_type, generation_method,
auto_config, manual_config, company_code
```

### table_type_columns

```sql
-- 핵심 컬럼
table_name, column_name, input_type, column_label,
detail_settings, company_code
```

---

## 참고 문서

### 필수 읽기

1. **[본서버_개발서버_마이그레이션_상세가이드.md](./본서버_개발서버_마이그레이션_상세가이드.md)** - 상세 마이그레이션 절차
2. **[화면개발_표준_가이드.md](../screen-implementation-guide/화면개발_표준_가이드.md)** - V2 화면 개발 표준
3. **[SCREEN_DEVELOPMENT_STANDARD.md](../screen-implementation-guide/SCREEN_DEVELOPMENT_STANDARD.md)** - 영문 표준 가이드

### 코드 참조

| 파일 | 설명 |
|------|------|
| `backend-node/src/services/categoryTreeService.ts` | 카테고리 관리 서비스 |
| `backend-node/src/services/numberingRuleService.ts` | 채번 규칙 서비스 |
| `frontend/lib/registry/components/v2-category-manager/` | V2 카테고리 컴포넌트 |
| `frontend/lib/registry/components/v2-numbering-rule/` | V2 채번 컴포넌트 |

### 관련 문서

- `docs/V2_컴포넌트_분석_가이드.md`
- `docs/V2_컴포넌트_연동_가이드.md`
- `docs/DDD1542/COMPONENT_LAYOUT_V2_ARCHITECTURE.md`
- `docs/DDD1542/COMPONENT_MIGRATION_PLAN.md`

---

## 주의사항

### 절대 하지 말 것

1. **개발서버 → 본서버 마이그레이션** (반대 방향)
2. **본서버 데이터 직접 수정** (SELECT만 허용)
3. **company_code 누락** (멀티테넌시 필수)
4. **테이블-컬럼 연결 없이 컴포넌트 배치** ("component"로 표시되면 실패)
5. **menu_objid 기반 카테고리/채번 사용** (V2는 table_name + column_name 기반)

### 반드시 할 것

1. 마이그레이션 전 **개발서버 백업**
2. 컴포넌트 변환 시 **V2 컴포넌트만 사용** (v2- prefix)
3. 모달 화면은 **부모 화면에 통합**
4. 카테고리/채번은 **table_name + column_name 기반**
5. 컴포넌트 배치 후 **"v2-xxx"로 표시되는지 반드시 확인**

### 실패 사례 (이전 작업자)

**물류정보관리 → 운송업체 관리 마이그레이션 실패**

- **원인**: 컴포넌트를 직접 배치하여 "component"로 생성됨
- **증상**: 화면에 "component" 라벨 표시, 데이터 바인딩 실패
- **해결**: 왼쪽 패널에서 테이블 컬럼을 드래그하여 "v2-input" 등으로 생성

---

## 🔧 일괄 수정 SQL (overrides.type 누락 문제)

### 문제 진단 쿼리

```sql
-- overrides.type이 없는 컴포넌트 수 확인
SELECT 
  COUNT(DISTINCT sv2.screen_id) as affected_screens,
  COUNT(*) as affected_components
FROM screen_layouts_v2 sv2, 
     jsonb_array_elements(sv2.layout_data->'components') as comp
WHERE (comp->>'url' LIKE '%/v2-input' 
       OR comp->>'url' LIKE '%/v2-select' 
       OR comp->>'url' LIKE '%/v2-date')
  AND NOT (comp->'overrides' ? 'type');
```

### 일괄 수정 쿼리 (개발서버에서만!)

```sql
UPDATE screen_layouts_v2 
SET layout_data = jsonb_set(
  layout_data,
  '{components}',
  (
    SELECT jsonb_agg(
      CASE 
        WHEN comp->>'url' LIKE '%/v2-input' AND NOT (comp->'overrides' ? 'type')
        THEN jsonb_set(comp, '{overrides,type}', '"v2-input"')
        WHEN comp->>'url' LIKE '%/v2-select' AND NOT (comp->'overrides' ? 'type')
        THEN jsonb_set(comp, '{overrides,type}', '"v2-select"')
        WHEN comp->>'url' LIKE '%/v2-date' AND NOT (comp->'overrides' ? 'type')
        THEN jsonb_set(comp, '{overrides,type}', '"v2-date"')
        WHEN comp->>'url' LIKE '%/v2-textarea' AND NOT (comp->'overrides' ? 'type')
        THEN jsonb_set(comp, '{overrides,type}', '"v2-textarea"')
        ELSE comp
      END
    )
    FROM jsonb_array_elements(layout_data->'components') comp
  )
),
updated_at = NOW()
WHERE EXISTS (
  SELECT 1 FROM jsonb_array_elements(layout_data->'components') c
  WHERE (c->>'url' LIKE '%/v2-input' OR c->>'url' LIKE '%/v2-select' 
         OR c->>'url' LIKE '%/v2-date' OR c->>'url' LIKE '%/v2-textarea')
    AND NOT (c->'overrides' ? 'type')
);
```

### 2026-02-04 일괄 수정 실행 결과

| 항목 | 수량 |
|------|------|
| 수정된 화면 | 397개 |
| 수정된 컴포넌트 | 2,455개 |
| v2-input | 1,983개 |
| v2-select | 336개 |
| v2-date | 136개 |

---

## 마이그레이션 진행 로그

| 날짜 | 메뉴 | 담당 | 상태 | 비고 |
|------|------|------|------|------|
| 2026-02-03 | 품질관리 | DDD1542 | 분석 완료 | 마이그레이션 대기 |
| 2026-02-03 | 물류관리 (운송업체) | 이전 신하 | ❌ 실패 | component 연결 오류 |
| 2026-02-03 | 문서 학습 | DDD1542 | ✅ 완료 | 핵심 4개 문서 정독, 학습노트 작성 |
| **2026-02-04** | **overrides.type 원인 분석** | **AI** | **✅ 완료** | **핵심 원인 발견: overrides.type 누락** |
| **2026-02-04** | **전체 입력폼 일괄 수정** | **AI** | **✅ 완료** | **397개 화면, 2,455개 컴포넌트 수정** |
| | 물류관리 | - | 미시작 | |
| | 생산관리 | - | 미시작 | |
| | 영업관리 | - | 미시작 | |

---

## 다음 작업 요청 예시

다음 AI에게 요청할 때 이렇게 말하면 됩니다:

```
"본서버_개발서버_마이그레이션_가이드.md 읽고 품질관리 메뉴 마이그레이션 진행해줘"

"본서버_개발서버_마이그레이션_가이드.md 참고해서 물류관리 메뉴 분석해줘"

"본서버_개발서버_마이그레이션_상세가이드.md 보고 COMPANY_7_142 화면 V2로 변환해줘"
```

---

## 변경 이력

| 날짜 | 작성자 | 내용 |
|------|--------|------|
| 2026-02-03 | DDD1542 | 초안 작성 |
| 2026-02-03 | DDD1542 | 컴포넌트-컬럼 연결 주의사항 추가 (이전 실패 원인) |
| 2026-02-03 | DDD1542 | 개인 학습노트 작성 (V2_마이그레이션_학습노트_DDD1542.md) |
| **2026-02-04** | **AI** | **핵심 원인 발견: overrides.type 필드 누락 문제** |
| **2026-02-04** | **AI** | **일괄 수정 SQL 추가 및 397개 화면 수정 완료** |
