- buttonActions.ts: formData가 배열인 경우 일반 저장 건너뜀
- SelectedItemsDetailInput이 UPSERT를 완료한 후 일반 저장이 실행되어 null 레코드가 삽입되던 문제 해결
- ScreenModal에서 그룹 레코드를 배열로 전달하는 경우 감지하여 처리
- skipDefaultSave 플래그가 제대로 작동하지 않던 문제 근본 해결
- SelectedItemsDetailInput 컴포넌트 수정 모드 지원
- 그룹화된 데이터 UPSERT API 추가 (/api/data/upsert-grouped)
- 부모 키 기준으로 기존 레코드 조회 후 INSERT/UPDATE/DELETE
- 각 레코드의 모든 필드 조합을 고유 키로 사용
- created_date 보존 (UPDATE 시)
- 수정 모드에서 groupByColumns 기준으로 관련 레코드 조회
- 날짜 타입 ISO 형식 자동 감지 및 포맷팅 (YYYY.MM.DD)
주요 변경사항:
- backend: dataService.upsertGroupedRecords() 메서드 구현
- backend: dataRoutes POST /api/data/upsert-grouped 엔드포인트 추가
- frontend: ScreenModal에서 groupByColumns 파라미터 전달
- frontend: SelectedItemsDetailInput 수정 모드 로직 추가
- frontend: 날짜 필드 타임존 제거 및 포맷팅 개선
4개 서비스 18개 Prisma 호출 전환 완료:
1. **EnhancedDynamicFormService** (6개)
- validateTableExists - information_schema 조회
- getTableColumns - 테이블 컬럼 정보 조회 with 캐싱
- getColumnWebTypes - 웹타입 정보 조회
- getPrimaryKeys - Primary Key 조회
- performInsert - 동적 INSERT with RETURNING
- performUpdate - 동적 UPDATE with RETURNING
2. **DataMappingService** (5개)
- getSourceData - 소스 테이블 데이터 조회
- executeInsert - 동적 INSERT
- executeUpsert - ON CONFLICT DO UPDATE
- executeUpdate - 동적 UPDATE
- disconnect - 제거 (Raw Query 불필요)
3. **DataService** (4개)
- getTableData - 동적 SELECT with 동적 WHERE/ORDER BY
- checkTableExists - information_schema 테이블 존재 확인
- getTableColumnsSimple - 컬럼 정보 조회
- getColumnLabel - 컬럼 라벨 조회
4. **AdminService** (3개)
- getAdminMenuList - WITH RECURSIVE 쿼리
- getUserMenuList - WITH RECURSIVE 쿼리
- getMenuInfo - LEFT JOIN으로 회사 정보 포함
기술적 성과:
- 변수명 충돌 해결 (query vs sql)
- WITH RECURSIVE 쿼리 전환
- Prisma include → LEFT JOIN 전환
- 동적 쿼리 생성 (WHERE, ORDER BY)
- SQL 인젝션 방지 (컬럼명 검증)
진행률: Phase 3 173/186 (93.0%)
문서: PHASE3.16_DATA_MANAGEMENT_SERVICES_MIGRATION.md