column_labels 테이블 제거 영향 분석
개요
현재 시스템은 컬럼 메타데이터를 두 개의 테이블에서 관리하고 있습니다:
column_labels: 레거시 테이블 (회사코드 없음, 공통 데이터)
table_type_columns: 새 테이블 (회사코드 있음, 멀티테넌시 지원)
이 문서는 column_labels 테이블을 제거하고 table_type_columns로 통합할 때의 영향을 분석합니다.
1. 두 테이블 스키마 비교
column_labels (레거시)
| 컬럼 |
타입 |
설명 |
| id |
INTEGER |
PK |
| table_name |
VARCHAR |
테이블명 |
| column_name |
VARCHAR |
컬럼명 |
| column_label |
VARCHAR |
한글 라벨 |
| web_type |
VARCHAR |
레거시 (사용 안 함) |
| input_type |
VARCHAR |
입력 타입 |
| detail_settings |
TEXT |
상세 설정 (JSON) |
| description |
TEXT |
설명 |
| display_order |
INTEGER |
표시 순서 |
| is_visible |
BOOLEAN |
표시 여부 |
| code_category |
VARCHAR |
코드 카테고리 |
| code_value |
VARCHAR |
코드 값 |
| reference_table |
VARCHAR |
참조 테이블 |
| reference_column |
VARCHAR |
참조 컬럼 |
| display_column |
VARCHAR |
표시 컬럼 |
| created_date |
TIMESTAMP |
생성일 |
| updated_date |
TIMESTAMP |
수정일 |
특징: company_code 없음 → 멀티테넌시 불가
table_type_columns (신규)
| 컬럼 |
타입 |
설명 |
| id |
INTEGER |
PK |
| table_name |
VARCHAR |
테이블명 |
| column_name |
VARCHAR |
컬럼명 |
| input_type |
VARCHAR |
입력 타입 |
| detail_settings |
TEXT |
상세 설정 (JSON) |
| is_nullable |
VARCHAR |
NULL 허용 |
| display_order |
INTEGER |
표시 순서 |
| company_code |
VARCHAR |
회사 코드 |
| created_date |
TIMESTAMP |
생성일 |
| updated_date |
TIMESTAMP |
수정일 |
특징: company_code 있음 → 멀티테넌시 지원
누락된 컬럼 (table_type_columns에 추가 필요)
| 컬럼 |
용도 |
| column_label |
한글 라벨 |
| description |
설명 |
| is_visible |
표시 여부 |
| code_category |
코드 카테고리 |
| code_value |
코드 값 |
| reference_table |
참조 테이블 (엔티티) |
| reference_column |
참조 컬럼 |
| display_column |
표시 컬럼 |
2. 영향 받는 파일 목록
백엔드 파일 (16개, 87회 참조)
| 파일 |
참조 횟수 |
영향도 |
용도 |
tableManagementService.ts |
21회 |
🔴 매우 높음 |
컬럼 조회/저장/업데이트 핵심 로직 |
screenGroupController.ts |
13회 |
🟡 중간 |
화면 그룹/메뉴 동기화 시 라벨 조회 |
masterDetailExcelService.ts |
7회 |
🟡 중간 |
엑셀 다운로드 시 엔티티 관계 조회 |
ddlExecutionService.ts |
7회 |
🟡 중간 |
테이블 생성/삭제 시 메타데이터 등록 |
tableManagementController.ts |
7회 |
🟡 중간 |
API 엔드포인트 |
screenManagementService.ts |
5회 |
🔴 높음 |
화면에서 컬럼 라벨 조회 |
entityJoinService.ts |
4회 |
🔴 높음 |
엔티티 조인 관계 감지 |
entityReferenceController.ts |
4회 |
🟡 중간 |
엔티티 참조 데이터 조회 |
adminController.ts |
3회 |
🟢 낮음 |
엑셀 업로드 컬럼 매핑 |
dataService.ts |
3회 |
🟢 낮음 |
컬럼 라벨 조회 |
flowController.ts |
3회 |
🟢 낮음 |
플로우 컬럼 라벨 조회 |
categoryTreeService.ts |
1회 |
🟢 낮음 |
카테고리 라벨 조회 |
tableManagementRoutes.ts |
1회 |
🟢 낮음 |
라우트 주석 |
multiConnectionQueryService.ts |
1회 |
🟢 낮음 |
멀티 연결 쿼리 |
migrate-input-type-to-web-type.ts |
6회 |
🟢 낮음 |
마이그레이션 스크립트 |
types/ddl.ts |
1회 |
🟢 낮음 |
타입 정의 |
프론트엔드 파일 (15개, 20회 참조)
| 파일 |
참조 횟수 |
영향도 |
용도 |
UnifiedRepeater.tsx |
3회 |
🟢 낮음 |
타입 주석 |
ScreenDesigner.tsx |
2회 |
🟢 낮음 |
타입 주석 |
ButtonConfigPanel.tsx |
2회 |
🟢 낮음 |
타입 주석 |
ScreenRelationFlow.tsx |
2회 |
🟢 낮음 |
타입 주석 |
buttonActions.ts |
1회 |
🟢 낮음 |
주석 |
webTypeMapping.ts |
1회 |
🟢 낮음 |
주석 |
screenGroup.ts |
1회 |
🟢 낮음 |
API 타입 |
tableManagement.ts |
1회 |
🟢 낮음 |
API 타입 |
TableSettingModal.tsx |
1회 |
🟢 낮음 |
주석 |
tableSchema.ts |
1회 |
🟢 낮음 |
타입 |
types/ddl.ts |
1회 |
🟢 낮음 |
타입 정의 |
ControlConditionStep.tsx |
1회 |
🟢 낮음 |
주석 |
ActionConditionBuilder.tsx |
1회 |
🟢 낮음 |
주석 |
WebTypeInput.tsx |
1회 |
🟢 낮음 |
주석 |
types/multiConnection.ts |
1회 |
🟢 낮음 |
타입 |
3. 주요 사용 패턴 분석
패턴 1: 컬럼 조회 (가장 많음)
-- 현재 방식: column_labels + table_type_columns 조인
SELECT
c.column_name,
COALESCE(cl.column_label, c.column_name) as "displayName",
COALESCE(ttc.input_type, cl.input_type, 'text') as "inputType",
COALESCE(ttc.detail_settings, cl.detail_settings) as "detailSettings",
cl.reference_table as "referenceTable" -- ❌ 문제: ttc의 detailSettings 무시
FROM information_schema.columns c
LEFT JOIN column_labels cl ON c.table_name = cl.table_name AND c.column_name = cl.column_name
LEFT JOIN table_type_columns ttc ON c.table_name = ttc.table_name
AND c.column_name = ttc.column_name
AND ttc.company_code = $company_code
문제점: referenceTable이 column_labels에서만 조회됨 → 회사별 엔티티 설정 무시
패턴 2: 컬럼 저장 (이중 저장)
// 현재: 두 테이블에 모두 저장
await query(`INSERT INTO column_labels (...) VALUES (...) ON CONFLICT ... DO UPDATE ...`);
await this.updateColumnInputType(...); // table_type_columns에도 저장
문제점: 데이터 불일치 가능성, 유지보수 어려움
패턴 3: 엔티티 관계 조회
SELECT column_name, reference_table, reference_column
FROM column_labels
WHERE table_name = $1 AND input_type = 'entity'
문제점: 회사별 엔티티 설정 무시 (column_labels에 company_code 없음)
4. 마이그레이션 계획
Phase 1: 스키마 확장 (table_type_columns)
-- 마이그레이션 파일: 044_extend_table_type_columns.sql
-- 1. 누락된 컬럼 추가
ALTER TABLE table_type_columns ADD COLUMN IF NOT EXISTS column_label VARCHAR(200);
ALTER TABLE table_type_columns ADD COLUMN IF NOT EXISTS description TEXT;
ALTER TABLE table_type_columns ADD COLUMN IF NOT EXISTS is_visible BOOLEAN DEFAULT true;
ALTER TABLE table_type_columns ADD COLUMN IF NOT EXISTS code_category VARCHAR(100);
ALTER TABLE table_type_columns ADD COLUMN IF NOT EXISTS code_value VARCHAR(100);
ALTER TABLE table_type_columns ADD COLUMN IF NOT EXISTS reference_table VARCHAR(100);
ALTER TABLE table_type_columns ADD COLUMN IF NOT EXISTS reference_column VARCHAR(100);
ALTER TABLE table_type_columns ADD COLUMN IF NOT EXISTS display_column VARCHAR(100);
-- 2. 인덱스 추가
CREATE INDEX IF NOT EXISTS idx_ttc_reference_table ON table_type_columns(reference_table);
CREATE INDEX IF NOT EXISTS idx_ttc_input_type ON table_type_columns(input_type);
Phase 2: 데이터 마이그레이션
-- column_labels 데이터를 table_type_columns로 이관 (company_code = '*' 로 공통 데이터)
INSERT INTO table_type_columns (
table_name, column_name, input_type, detail_settings,
column_label, description, is_visible, code_category, code_value,
reference_table, reference_column, display_column, display_order,
company_code, created_date, updated_date
)
SELECT
table_name, column_name,
COALESCE(input_type, 'text'),
detail_settings,
column_label,
description,
COALESCE(is_visible, true),
code_category,
code_value,
reference_table,
reference_column,
display_column,
display_order,
'*', -- 공통 데이터 (회사별 설정 없으면 폴백)
COALESCE(created_date, NOW()),
COALESCE(updated_date, NOW())
FROM column_labels
ON CONFLICT (table_name, column_name, company_code)
DO UPDATE SET
column_label = COALESCE(EXCLUDED.column_label, table_type_columns.column_label),
description = COALESCE(EXCLUDED.description, table_type_columns.description),
reference_table = COALESCE(EXCLUDED.reference_table, table_type_columns.reference_table),
reference_column = COALESCE(EXCLUDED.reference_column, table_type_columns.reference_column),
display_column = COALESCE(EXCLUDED.display_column, table_type_columns.display_column),
updated_date = NOW();
Phase 3: 코드 수정
3.1 조회 쿼리 변경 패턴
-- 변경 전
SELECT ... FROM column_labels WHERE table_name = $1
-- 변경 후 (회사코드 폴백 포함)
SELECT * FROM (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY table_name, column_name
ORDER BY CASE WHEN company_code = $company_code THEN 0 ELSE 1 END
) as rn
FROM table_type_columns
WHERE table_name = $1
AND company_code IN ($company_code, '*')
) ranked
WHERE rn = 1
3.2 저장 쿼리 변경 패턴
-- 변경 전: 두 테이블에 저장
INSERT INTO column_labels (...) ...;
INSERT INTO table_type_columns (...) ...;
-- 변경 후: 하나의 테이블만
INSERT INTO table_type_columns (
table_name, column_name, input_type, detail_settings,
column_label, description, is_visible, code_category, code_value,
reference_table, reference_column, display_column, display_order,
company_code, created_date, updated_date
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, NOW(), NOW())
ON CONFLICT (table_name, column_name, company_code)
DO UPDATE SET ...;
Phase 4: 레거시 코드 정리
column_labels 관련 INSERT/UPDATE 제거
column_labels LEFT JOIN을 table_type_columns로 변경
- 프론트엔드 주석/타입 업데이트
Phase 5: 테이블 삭제 (최종)
-- 모든 코드 마이그레이션 완료 후
DROP TABLE IF EXISTS column_labels;
5. 수정해야 할 파일 상세
🔴 우선순위 1: 핵심 서비스 (3개)
tableManagementService.ts (21개 쿼리)
| 함수 |
라인 |
수정 내용 |
checkCodeTypeColumn |
30-36 |
column_labels → table_type_columns |
getColumnList |
215-218, 257-258 |
JOIN 변경 + referenceTable 추출 |
updateColumnSettings |
460-494 |
column_labels INSERT 제거 |
getColumnLabels |
670-673 |
table_type_columns로 변경 |
setColumnInputType |
735-740 |
column_labels INSERT 제거 |
getFileColumns |
1288 |
table_type_columns로 변경 |
getColumnMetaInfo |
1956 |
table_type_columns로 변경 |
findEntityRelation |
3579-3590 |
table_type_columns로 변경 |
upsertColumnLabel |
3723 |
table_type_columns로 변경 |
getColumnInputTypes |
4129 |
이미 table_type_columns 사용 중 ✅ |
detectEntityRelation |
4810, 4838 |
table_type_columns로 변경 |
screenManagementService.ts (5개 쿼리)
| 함수 |
라인 |
수정 내용 |
getTableColumns |
1279 |
table_type_columns로 변경 |
getTableColumns |
1334 |
라벨 추가 로직 수정 |
getColumnInfo |
2083 |
table_type_columns로 변경 |
saveColumnSettings |
2104 |
table_type_columns로 변경 |
entityJoinService.ts (4개 쿼리)
| 함수 |
라인 |
수정 내용 |
detectEntityColumns |
36 |
table_type_columns로 변경 |
getTableColumns |
755 |
table_type_columns로 변경 |
🟡 우선순위 2: 보조 서비스 (5개)
| 파일 |
수정 쿼리 수 |
수정 내용 |
screenGroupController.ts |
13개 |
라벨 조회, FK 조회 쿼리 변경 |
masterDetailExcelService.ts |
7개 |
엔티티 관계 조회 변경 |
ddlExecutionService.ts |
7개 |
테이블 생성 시 메타데이터 등록 변경 |
entityReferenceController.ts |
4개 |
참조 데이터 조회 변경 |
adminController.ts |
3개 |
스키마 조회 변경 |
🟢 우선순위 3: 기타 (8개)
| 파일 |
수정 내용 |
dataService.ts |
라벨 조회 변경 |
flowController.ts |
라벨 조회 변경 |
categoryTreeService.ts |
JOIN 변경 |
tableManagementRoutes.ts |
주석 수정 |
multiConnectionQueryService.ts |
주석/타입 수정 |
migrate-input-type-to-web-type.ts |
마이그레이션 스크립트 (이미 실행됨, 삭제 가능) |
types/ddl.ts |
타입 정의 수정 |
| 프론트엔드 15개 파일 |
주석/타입 수정 |
6. 예상 작업 시간
| 단계 |
작업 |
예상 시간 |
| Phase 1 |
스키마 확장 (마이그레이션 SQL) |
30분 |
| Phase 2 |
데이터 마이그레이션 (SQL) |
30분 |
| Phase 3.1 |
tableManagementService.ts 수정 |
2시간 |
| Phase 3.2 |
screenManagementService.ts 수정 |
1시간 |
| Phase 3.3 |
entityJoinService.ts 수정 |
30분 |
| Phase 3.4 |
보조 서비스 5개 수정 |
2시간 |
| Phase 3.5 |
기타 파일 8개 수정 |
1시간 |
| Phase 4 |
테스트 및 검증 |
2시간 |
| Phase 5 |
column_labels 삭제 |
10분 |
| 합계 |
|
약 10시간 |
7. 리스크 및 주의사항
높은 리스크
- 데이터 불일치: 마이그레이션 중 column_labels와 table_type_columns 데이터 충돌 가능
- 회사코드 폴백 로직 복잡성: 모든 조회에
company_code IN ($code, '*') + 우선순위 필요
- 기존 운영 데이터 손실: 마이그레이션 실수 시 column_labels 데이터 유실 가능
완화 방안
- 단계적 마이그레이션: column_labels는 당분간 유지, 조회만 table_type_columns 우선으로 변경
- 폴백 헬퍼 함수: 회사코드 폴백 로직을 공통 함수로 추출
- 백업 필수: 마이그레이션 전 column_labels 전체 백업
8. 결론
현재 문제점
- 이중 관리: 같은 데이터가 두 테이블에 저장됨
- 멀티테넌시 불완전:
referenceTable 등이 column_labels에서만 조회되어 회사별 설정 무시
- 유지보수 어려움: 변경 시 두 곳 모두 수정 필요
권장 방향
장기적으로 table_type_columns로 통합 권장
하지만 작업량이 상당하므로:
- 단기 (즉시): 조회 시
detailSettings에서 referenceTable 우선 추출하도록 수정
- 중기 (1-2주):
table_type_columns 스키마 확장 + 데이터 마이그레이션
- 장기 (한 달): 모든 코드 수정 후
column_labels 제거
참고 자료
table_type_columns 관련 마이그레이션: db/migrations/030_create_table_type_columns.sql
- 테이블 타입 관리 UI:
frontend/app/(main)/admin/systemMng/tableMngList/page.tsx
- 컬럼 조회 핵심 로직:
backend-node/src/services/tableManagementService.ts:getColumnList()