- category_column_mapping 테이블 생성 (마이그레이션 054)
- 테이블 타입 관리에서 2레벨 메뉴 선택 기능 추가
- 카테고리 컬럼 조회 시 현재 메뉴 및 상위 메뉴 매핑 자동 조회
- 캐시 무효화 로직 개선
- 메뉴별 카테고리 설정 저장 및 불러오기 기능 구현
## 주요 변경사항
### 1. 화면 복사 기능 강화
- 최고 관리자가 다른 회사로 화면 복사 가능하도록 개선
- 메인 화면과 연결된 모달 화면 자동 감지 및 일괄 복사
- 복사 시 버튼의 targetScreenId 자동 업데이트
- 일괄 이름 변경 기능 추가 (복사본 텍스트 제거)
- 중복 화면명 체크 기능 추가
#### 백엔드 (screenManagementService.ts)
- generateMultipleScreenCodes: 여러 화면 코드 일괄 생성 (Advisory Lock 사용)
- detectLinkedModalScreens: edit 액션도 모달로 감지하도록 개선
- checkDuplicateScreenName: 중복 화면명 체크 API 추가
- copyScreenWithModals: 메인+모달 일괄 복사 및 버튼 업데이트
- updateButtonTargetScreenIds: 복사된 모달로 버튼 targetScreenId 업데이트
- updated_date 컬럼 제거 (screen_layouts 테이블에 존재하지 않음)
#### 프론트엔드 (CopyScreenModal.tsx)
- 회사 선택 UI 추가 (최고 관리자 전용)
- 연결된 모달 화면 자동 감지 및 표시
- 일괄 이름 변경 기능 (텍스트 제거/추가)
- 실시간 미리보기
- 중복 화면명 체크
### 2. 버튼 설정 모달 화면 선택 개선
- 편집 중인 화면의 company_code 기준으로 화면 목록 조회
- 최고 관리자가 다른 회사 화면 편집 시 해당 회사의 모달 화면만 표시
- targetScreenId 문자열/숫자 타입 불일치 수정
#### 백엔드 (screenManagementController.ts)
- getScreens API에 companyCode 쿼리 파라미터 추가
- 최고 관리자는 다른 회사의 화면 목록 조회 가능
#### 프론트엔드
- ButtonConfigPanel: currentScreenCompanyCode props 추가
- DetailSettingsPanel: currentScreenCompanyCode 전달
- UnifiedPropertiesPanel: currentScreenCompanyCode 전달
- ScreenDesigner: selectedScreen.companyCode 전달
- targetScreenId 비교 시 parseInt 처리 (문자열→숫자)
### 3. 카테고리 메뉴별 컬럼 분리 기능
- 메뉴별로 카테고리 컬럼을 독립적으로 관리
- 카테고리 컬럼 추가/삭제 시 메뉴 스코프 적용
## 수정된 파일
- backend-node/src/services/screenManagementService.ts
- backend-node/src/controllers/screenManagementController.ts
- backend-node/src/routes/screenManagementRoutes.ts
- frontend/components/screen/CopyScreenModal.tsx
- frontend/components/screen/config-panels/ButtonConfigPanel.tsx
- frontend/components/screen/panels/DetailSettingsPanel.tsx
- frontend/components/screen/panels/UnifiedPropertiesPanel.tsx
- frontend/components/screen/ScreenDesigner.tsx
- frontend/lib/api/screen.ts
**주요 변경사항:**
1. **메뉴 스코프 변경 (getSiblingMenuObjids)**
- 기존: 형제 메뉴 + 모든 형제의 자식 메뉴 포함
- 변경: 자신 + 자신의 자식 메뉴만 포함
- 결과: 각 2레벨 메뉴가 완전히 독립적으로 격리됨
2. **채번 규칙 메뉴 선택 상태 유지**
- useState 초기값 함수에서 저장된 selectedMenuObjid 복원
- 속성창 닫았다 열어도 선택한 메뉴와 채번 규칙 유지
- config.autoGeneration.selectedMenuObjid에 저장
3. **로그 정리**
- 프론트엔드: 디버깅 로그 제거
- 백엔드: info 레벨 로그를 debug 레벨로 변경
- 운영 환경에서 불필요한 로그 출력 최소화
**영향:**
- 영업관리 메뉴: 영업관리의 채번 규칙/코드만 조회
- 기준정보 메뉴: 기준정보의 채번 규칙/코드만 조회
- 각 메뉴 그룹이 독립적으로 데이터 관리 가능
- 백엔드: screenManagementService에 getMenuByScreen 함수 추가
- 백엔드: GET /api/screen-management/screens/:id/menu 엔드포인트 추가
- 프론트엔드: screenApi.getScreenMenu() 함수 추가
- ScreenDesigner: 화면 로드 시 menu_objid 자동 조회
- ScreenDesigner: menuObjid를 RealtimePreview와 UnifiedPropertiesPanel에 전달
- UnifiedPropertiesPanel: menuObjid를 DynamicComponentConfigPanel에 전달
이로써 화면 편집기에서 코드/카테고리/채번규칙이 해당 화면이 할당된 메뉴 기준으로 필터링됨
- frontend: screen.ts에 saveScreenLayout 함수 추가 (ScreenDesigner_new.tsx가 호출하던 누락된 함수)
- frontend: ScreenDesigner_new.tsx 저장 시 디버깅 로그 추가
- backend: screenManagementService.ts에 dataflowConfig 저장 확인 로그 추가
문제 원인:
- ScreenDesigner_new.tsx가 호출하던 screenApi.saveScreenLayout 함수가 정의되지 않음
- 이로 인해 레이아웃 저장이 실패했을 가능성
해결:
- saveScreenLayout 함수를 추가하여 정상적인 레이아웃 저장 가능
- 디버깅 로그를 통해 실제로 selectedDiagramId가 저장되는지 확인 가능
추가 전환 완료:
✅ 조회 및 관리 함수들:
- checkScreenDependencies() - 화면 의존성 확인 (JOIN 쿼리)
- cleanupDeletedScreenMenuAssignments() - 메뉴 할당 정리
- permanentDeleteScreen() - 영구 삭제 (트랜잭션)
- getDeletedScreens() - 휴지통 목록 조회 (페이징 + 테이블 레이블)
📊 진행률: 25+/46 (54%+)
🎯 다음: $queryRaw 함수들 전환 (테이블/컬럼 정보 조회)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Stage 4: 트랜잭션 기반 복잡한 기능 Raw Query 전환
✅ 전환 완료 (3개 트랜잭션 함수):
**트랜잭션 함수들:**
1. copyScreen() - 화면 복사 (화면 + 레이아웃 전체 복사)
- 원본 화면 조회 (SELECT)
- 화면 코드 중복 체크 (SELECT)
- 새 화면 생성 (INSERT RETURNING)
- 원본 레이아웃 조회 (SELECT with ORDER BY)
- ID 매핑 후 레이아웃 복사 (반복 INSERT)
- PoolClient 기반 트랜잭션 사용
2. restoreScreen() - 삭제된 화면 복원
- 화면 코드 중복 체크 (SELECT with multiple conditions)
- 화면 복원 (UPDATE with NULL 설정)
- 메뉴 할당 활성화 (UPDATE)
- 트랜잭션으로 원자성 보장
3. bulkDeletePermanently() - 일괄 영구 삭제
- 삭제 대상 조회 (SELECT with dynamic WHERE)
- 레이아웃 삭제 (DELETE)
- 메뉴 할당 삭제 (DELETE)
- 화면 정의 삭제 (DELETE)
- 각 화면마다 개별 트랜잭션으로 롤백 격리
📊 진행률: 20+/46 (43%+)
🎯 다음: 나머지 Prisma 호출 전환 (조회, UPSERT 등)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
추가 기본 조회 함수 Raw Query 전환
✅ 추가 전환 완료 (2개):
7. getScreens() - 전체 화면 목록 조회 (동적 WHERE)
8. getScreen() - 회사 코드 필터링 포함 조회
📊 진행률: 8/46 (17.4%)
🎯 다음: Stage 2 레이아웃 관리 전환
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
1단계 기본 CRUD 전환 시작 (2/6 완료)
✅ 전환 완료 (2개):
1. createScreen() - 화면 생성
- 중복 확인: findFirst → Raw Query SELECT
- 생성: create → Raw Query INSERT RETURNING
- 파라미터 바인딩 적용
2. getScreensByCompany() - 화면 목록 조회 (페이징)
- 동적 WHERE 절 생성
- Promise.all로 병렬 조회 (목록 + 총개수)
- table_labels IN 쿼리 전환
🔧 주요 변경사항:
- Prisma import 제거 → query, transaction import
- 파라미터 바인딩으로 SQL Injection 방지
- COUNT 결과 문자열 → 숫자 변환
📊 진행률:
- 전환 완료: 2/46 (4.3%)
- 남은 작업: 44개 Prisma 호출
🎯 다음 작업:
- getScreenByCode()
- getScreenById()
- updateScreen()
- deleteScreen()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>