# 카테고리 컴포넌트 DB 호환성 분석 및 수정

> **작성일**: 2025-11-04  
> **상태**: 호환성 문제 발견 및 수정 완료

---

## 발견된 호환성 문제

### 1. 테이블명 불일치

| 예상 테이블명 | 실제 테이블명 | 상태 |
|--------------|--------------|------|
| `table_columns` | `table_type_columns` | ❌ 불일치 |
| `company_info` | `company_mng` | ❌ 불일치 |

### 2. 컬럼명 불일치

#### table_type_columns 테이블

| 예상 컬럼명 | 실제 컬럼명 | 상태 |
|------------|------------|------|
| `column_label` | 존재하지 않음 | ❌ 불일치 |
| `web_type` | `input_type` | ❌ 불일치 |
| `column_order` | `display_order` | ❌ 불일치 |
| `company_code` | 존재하지 않음 | ❌ 불일치 |

**실제 table_type_columns 구조**:
```sql
- id (integer, PK)
- table_name (varchar(255), NOT NULL)
- column_name (varchar(255), NOT NULL)
- input_type (varchar(50), NOT NULL, DEFAULT 'text')
- detail_settings (text)
- is_nullable (varchar(10), DEFAULT 'Y')
- display_order (integer, DEFAULT 0)
- created_date (timestamp, DEFAULT now())
- updated_date (timestamp, DEFAULT now())
```

#### company_mng 테이블

| 예상 컬럼명 | 실제 컬럼명 | 상태 |
|------------|------------|------|
| `company_code` | `company_code` | ✅ 일치 |
| `company_name` | `company_name` | ✅ 일치 |

**실제 company_mng 구조**:
```sql
- company_code (varchar(32), PK)
- company_name (varchar(64))
- writer (varchar(32))
- regdate (timestamp)
- status (varchar(32))
- business_registration_number (varchar(20))
- representative_name (varchar(100))
- representative_phone (varchar(20))
- email (varchar(255))
- website (varchar(500))
- address (varchar(500))
```

---

## 적용된 수정사항

### 1. 마이그레이션 파일 수정

**파일**: `db/migrations/036_create_table_column_category_values.sql`

**변경사항**:
```sql
-- 변경 전
CONSTRAINT fk_category_value_company FOREIGN KEY (company_code) 
  REFERENCES company_info(company_code),

-- 변경 후
CONSTRAINT fk_category_value_company FOREIGN KEY (company_code) 
  REFERENCES company_mng(company_code),
```

### 2. 백엔드 서비스 수정

**파일**: `backend-node/src/services/tableCategoryValueService.ts`

**변경사항**:
```typescript
// 변경 전
const query = `
  SELECT 
    tc.table_name AS "tableName",
    tc.column_name AS "columnName",
    tc.column_label AS "columnLabel",
    COUNT(cv.value_id) AS "valueCount"
  FROM table_columns tc
  LEFT JOIN table_column_category_values cv 
    ON tc.table_name = cv.table_name 
    AND tc.column_name = cv.column_name
    AND cv.is_active = true
    AND (cv.company_code = $2 OR cv.company_code = '*')
  WHERE tc.table_name = $1
    AND tc.web_type = 'category'
    AND (tc.company_code = $2 OR tc.company_code = '*')
  GROUP BY tc.table_name, tc.column_name, tc.column_label, tc.column_order
  ORDER BY tc.column_order, tc.column_label
`;

// 변경 후
const query = `
  SELECT 
    tc.table_name AS "tableName",
    tc.column_name AS "columnName",
    tc.column_name AS "columnLabel",  -- column_label이 없으므로 column_name 사용
    COUNT(cv.value_id) AS "valueCount"
  FROM table_type_columns tc  -- table_columns → table_type_columns
  LEFT JOIN table_column_category_values cv 
    ON tc.table_name = cv.table_name 
    AND tc.column_name = cv.column_name
    AND cv.is_active = true
    AND (cv.company_code = $2 OR cv.company_code = '*')
  WHERE tc.table_name = $1
    AND tc.input_type = 'category'  -- web_type → input_type
  GROUP BY tc.table_name, tc.column_name, tc.display_order  -- column_order → display_order
  ORDER BY tc.display_order, tc.column_name
`;
```

---

## 주요 차이점 분석

### 1. 멀티테넌시 방식

**예상**: 모든 테이블에 `company_code` 컬럼 존재
**실제**: `table_type_columns`에는 `company_code` 컬럼이 없음

**영향**:
- 카테고리 컬럼 조회 시 회사별 필터링 불가
- 모든 회사가 동일한 테이블 구조 사용
- 카테고리 **값**만 회사별로 분리됨 (의도된 설계로 보임)

**결론**: ✅ 정상 - 테이블 구조는 공통, 데이터만 회사별 분리

### 2. 라벨 관리

**예상**: `table_columns.column_label` 컬럼에 라벨 저장
**실제**: `column_label` 컬럼 없음

**해결책**:
- 현재는 `column_name`을 그대로 라벨로 사용
- 필요 시 향후 `table_labels` 테이블과 JOIN하여 라벨 조회 가능

### 3. 타입 컬럼명

**예상**: `web_type`
**실제**: `input_type`

**결론**: ✅ 수정 완료 - `input_type` 사용으로 변경

---

## 테스트 계획

### 1. 마이그레이션 테스트

```sql
-- 1. 마이그레이션 실행
\i db/migrations/036_create_table_column_category_values.sql

-- 2. 테이블 생성 확인
SELECT table_name 
FROM information_schema.tables 
WHERE table_name = 'table_column_category_values';

-- 3. 외래키 제약조건 확인
SELECT
    tc.constraint_name,
    tc.table_name,
    kcu.column_name,
    ccu.table_name AS foreign_table_name,
    ccu.column_name AS foreign_column_name
FROM information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
    ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
    ON ccu.constraint_name = tc.constraint_name
WHERE tc.table_name = 'table_column_category_values'
    AND tc.constraint_type = 'FOREIGN KEY';
```

### 2. 카테고리 타입 컬럼 생성

먼저 테스트용 테이블에 카테고리 타입 컬럼을 추가해야 합니다:

```sql
-- 테스트용 projects 테이블이 없으면 생성
CREATE TABLE IF NOT EXISTS projects (
  id SERIAL PRIMARY KEY,
  project_name VARCHAR(200) NOT NULL,
  project_type VARCHAR(50),  -- 카테고리 타입
  project_status VARCHAR(50), -- 카테고리 타입
  priority VARCHAR(50),       -- 카테고리 타입
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- table_type_columns에 카테고리 타입 등록
INSERT INTO table_type_columns (table_name, column_name, input_type, display_order)
VALUES 
  ('projects', 'project_type', 'category', 1),
  ('projects', 'project_status', 'category', 2),
  ('projects', 'priority', 'category', 3)
ON CONFLICT DO NOTHING;
```

### 3. API 테스트

```bash
# 1. 카테고리 컬럼 목록 조회
curl -X GET http://localhost:8080/api/table-categories/projects/columns \
  -H "Authorization: Bearer YOUR_TOKEN"

# 예상 응답:
# {
#   "success": true,
#   "data": [
#     {
#       "tableName": "projects",
#       "columnName": "project_type",
#       "columnLabel": "project_type",
#       "valueCount": 4
#     },
#     {
#       "tableName": "projects",
#       "columnName": "project_status",
#       "columnLabel": "project_status",
#       "valueCount": 4
#     },
#     {
#       "tableName": "projects",
#       "columnName": "priority",
#       "columnLabel": "priority",
#       "valueCount": 4
#     }
#   ]
# }

# 2. 카테고리 값 목록 조회
curl -X GET http://localhost:8080/api/table-categories/projects/project_type/values \
  -H "Authorization: Bearer YOUR_TOKEN"

# 예상 응답:
# {
#   "success": true,
#   "data": [
#     {
#       "valueId": 1,
#       "valueCode": "DEV",
#       "valueLabel": "개발",
#       "color": "#3b82f6",
#       ...
#     }
#   ]
# }
```

---

## 추가 고려사항

### 1. 라벨 표시 개선

현재는 `columnName`을 그대로 라벨로 사용하지만, 더 나은 사용자 경험을 위해 다음 개선 가능:

**옵션 A**: `table_labels` 테이블 활용
```sql
SELECT 
  tc.table_name AS "tableName",
  tc.column_name AS "columnName",
  COALESCE(tl.table_label, tc.column_name) AS "columnLabel",
  COUNT(cv.value_id) AS "valueCount"
FROM table_type_columns tc
LEFT JOIN table_labels tl 
  ON tc.table_name = tl.table_name
LEFT JOIN table_column_category_values cv 
  ON tc.table_name = cv.table_name 
  AND tc.column_name = cv.column_name
WHERE tc.table_name = $1
  AND tc.input_type = 'category'
GROUP BY tc.table_name, tc.column_name, tl.table_label, tc.display_order
ORDER BY tc.display_order;
```

**옵션 B**: `detail_settings`에서 라벨 추출
```sql
SELECT 
  tc.table_name AS "tableName",
  tc.column_name AS "columnName",
  COALESCE(
    (tc.detail_settings::jsonb->>'label')::text,
    tc.column_name
  ) AS "columnLabel"
FROM table_type_columns tc
WHERE tc.table_name = $1
  AND tc.input_type = 'category';
```

### 2. input_type = 'category' 추가

현재 `input_type`에 `'category'` 값이 있는지 확인 필요:

```sql
-- 현재 사용 중인 input_type 확인
SELECT DISTINCT input_type 
FROM table_type_columns 
ORDER BY input_type;
```

만약 `'category'` 타입이 없다면, 기존 시스템에 추가해야 합니다.

---

## 호환성 체크리스트

### 데이터베이스
- [x] `company_mng` 테이블 존재 확인
- [x] `table_type_columns` 테이블 구조 확인
- [x] 외래키 참조 수정 (`company_info` → `company_mng`)
- [ ] `input_type = 'category'` 추가 여부 확인
- [ ] 테스트 데이터 삽입 확인

### 백엔드
- [x] 테이블명 수정 (`table_columns` → `table_type_columns`)
- [x] 컬럼명 수정 (`web_type` → `input_type`)
- [x] 컬럼명 수정 (`column_order` → `display_order`)
- [x] 라벨 처리 수정 (`column_label` → `column_name`)
- [ ] 멀티테넌시 로직 확인

### 프론트엔드
- [ ] API 응답 구조 확인
- [ ] 라벨 표시 테스트
- [ ] UI 테스트

---

## 결론

✅ **호환성 문제 수정 완료**

주요 변경사항:
1. `company_info` → `company_mng` (외래키)
2. `table_columns` → `table_type_columns` (테이블명)
3. `web_type` → `input_type` (컬럼명)
4. `column_order` → `display_order` (컬럼명)
5. `column_label` → `column_name` (라벨 처리)

**다음 단계**:
1. 마이그레이션 실행
2. 테스트용 카테고리 컬럼 생성
3. API 테스트
4. 프론트엔드 테스트

