1. Select Basic 다중선택 컴포넌트 높이 문제 해결
- 외부 wrapper에 height: 100% 추가
- 내부 div에 인라인 스타일로 height: 100% 명시
- items-center 추가하여 태그 세로 가운데 정렬
- Tailwind h-full 클래스 제거로 스타일 충돌 방지
2. 메뉴 복사 시 menu_objid=0 공통 카테고리 타입 처리
- menu_objid가 숫자 0, 문자열 '0' 모두 처리
- == 0 타입 강제 변환으로 모든 경우 감지
- 카테고리 컬럼 매핑, 카테고리 값 모두 적용
- 공통 카테고리 19개 정상 복사 가능
- 백엔드: 배열 객체 형식 Repeater 데이터 처리 로직 추가
- 백엔드: Repeater 저장 시 company_code 자동 주입
- 백엔드: 부모 테이블 데이터 자동 병합 (targetTable = tableName)
- 프론트엔드: beforeFormSave 이벤트로 formData 주입
- 프론트엔드: _targetTable 메타데이터 전달
- 프론트엔드: ComponentRendererProps 상속 및 Renderer 단순화
멀티테넌시 및 부모-자식 관계 자동 처리로
복잡한 배열 데이터 저장 안정성 확보
문제:
- 테이블에서 'CATEGORY_218152,CATEGORY_205381' 같은 다중 값이
- 배지로 표시되지 않고 코드값 그대로 보임
원인:
- formatCellValue의 카테고리 렌더링이 단일 값만 처리
- 콤마로 구분된 다중 값 파싱 로직 없음
해결:
1. 콤마 구분자 감지 및 값 배열로 분리
2. 단일 값: 기존 로직 유지 (단일 배지)
3. 다중 값: flex-wrap gap-1로 여러 배지 렌더링
4. 각 배지는 매핑된 라벨과 색상 사용
결과:
✅ 다중선택 저장된 데이터가 테이블에서 여러 배지로 표시됨
✅ 각 배지에 올바른 색상과 라벨 적용
✅ 단일 값도 기존처럼 정상 작동
문제:
- value: undefined, label: undefined로 나옴
- v.categoryValue, v.categoryLabel이 존재하지 않음
디버깅:
- API 응답의 첫 번째 항목 전체 출력
- 객체의 모든 키 목록 출력
- 여러 가능한 속성명 시도:
- category_value / categoryValue / value
- category_label / categoryLabel / label
다음 단계:
- 콘솔에서 원본 데이터 구조 확인
- 실제 속성명에 맞게 매핑 수정
문제:
- select-basic이 webType='category'일 때 옵션이 안 보임
- CATEGORY_218152 같은 코드값만 표시됨
- 체크박스는 보이지만 라벨이 비어있음
원인:
- select-basic은 useCodeOptions만 사용 (code 타입용)
- category 타입은 getCategoryValues API 필요
해결:
1. categoryOptions 상태 추가
2. webType === 'category'일 때 getCategoryValues 호출
3. getAllOptions에 categoryOptions 포함
4. 로딩 상태에 isLoadingCategories 추가
디버깅:
- 카테고리 로딩 시작/완료 로그
- API 응답 로그
- 최종 allOptions 로그 추가
다음 단계:
- 콘솔에서 categoryOptions가 제대로 로드되는지 확인
문제:
- 다중선택 설정했지만 UI에 반영 안됨
- 디버깅 로그가 콘솔에 전혀 안 보임
원인 추정:
- SelectBasicComponent가 아예 렌더링 안되고 있을 가능성
- 또는 다른 select 컴포넌트가 대신 렌더링될 가능성
테스트:
- 최상단에 눈에 띄는 로그 (🚨🚨🚨) 추가
- componentId, componentType, columnName, multiple 값 출력
- 이 로그가 안 보이면 다른 컴포넌트가 렌더링되는 것
더 명확한 로그 출력:
- 단계별로 구분된 로그
- 각 props 출처별로 명확히 표시
- 최종 isMultiple 값 강조
- 활성화/비활성화 상태 명확히 표시
사용자는 브라우저 콘솔에서 다음을 확인:
1. '🔍 [SelectBasicComponent] ========== 다중선택 디버깅 ==========' 로그 찾기
2. '최종 isMultiple 값' 확인
3. 각 props 출처의 multiple 값 확인
4. ✅/❌ 상태 메시지 확인
기능:
- 설정 패널에 '다중 선택' 체크박스 추가
- multiple 옵션 활성화 시 다중선택 UI 렌더링
- 선택된 항목을 태그 형식으로 표시
- 각 태그에 X 버튼으로 개별 제거 가능
- 드롭다운에 체크박스 표시
- 콤마(,) 구분자로 값 저장/파싱
수정사항:
- SelectBasicConfigPanel: 다중 선택 체크박스 추가
- SelectBasicConfigPanel: config 병합 방식으로 변경 (다른 속성 보호)
- SelectBasicComponent: 초기값 콤마 구분자로 파싱
- SelectBasicComponent: 외부 value 변경 시 다중선택 배열 동기화
- SelectBasicComponent: 다중선택 UI 렌더링 로직 추가
사용법:
1. 설정 패널에서 '다중 선택' 체크
2. 드롭다운에서 여러 항목 선택
3. 선택된 항목이 태그로 표시되며 X로 제거 가능
4. 저장 시 '값1,값2,값3' 형식으로 저장
- displayMode가 undefined일 때 기본값 'list' 처리 누락
- 조건문을 (config.rightPanel?.displayMode || 'list') === 'list'로 변경
- 이제 처음 들어갔을 때부터 라벨 표시 설정 UI가 보임
문제: LIST 모드가 기본값인데 초기에는 설정 UI가 안 보이고 테이블 모드로 변경 후 다시 LIST로 바꿔야 보임
원인: undefined === 'list'가 false가 되어 조건문이 작동하지 않음
해결: 기본값 처리 추가
- 초기 렌더링 시 기존 필드들의 autoFillFromTable이 설정되어 있으면 컬럼 자동 로드
- useEffect로 localFields 초기화 시점에 모든 필드 순회하며 컬럼 로드
- 사용자가 저장된 설정을 열었을 때 즉시 컬럼 목록 표시
문제: 품목정보 테이블을 선택했지만 컬럼이 표시되지 않음
원인: 기존에 설정된 autoFillFromTable에 대한 컬럼이 초기 로드되지 않음
해결: 초기화 useEffect 추가로 기존 설정 복원
- types.ts에 targetTable 필드 추가하여 config에 저장되도록 수정
- ConfigPanel에서 targetTable을 localConfig로 관리하여 설정 유지
- Renderer 단순화 (TextInput 패턴 적용)
- Component에서 직접 isInteractive 체크 및 필드 매핑 처리
- ComponentRendererProps 상속으로 필수 props 타입 안정성 확보
문제:
- ConfigPanel 설정이 초기화되는 문제
- 필드 매핑 데이터가 DB에 저장되지 않는 문제
해결:
- 정상 작동하는 TextInput 컴포넌트 패턴 분석 및 적용
- Renderer는 props만 전달, Component가 저장 로직 처리
- 추가 입력 필드에서 자동 채우기 테이블을 드롭다운으로 선택 가능
- 텍스트 입력 대신 allTables에서 선택하는 방식으로 개선
- 테이블 선택 시 해당 테이블의 컬럼을 자동으로 로드
- autoFillTableColumns 상태로 필드별 테이블 컬럼 관리
- 선택한 테이블에 따라 컬럼 드롭다운이 동적으로 변경됨
사용자 경험 개선:
- 테이블명을 직접 입력하는 대신 목록에서 선택
- 선택한 테이블의 컬럼만 표시되어 혼란 방지
- 원본 테이블(기본) 또는 다른 테이블 선택 가능
- 원본 테이블(sourceTable) 변경 시 컬럼 자동 로드
- 대상 테이블(targetTable) 변경 시 컬럼 자동 로드
- props로 받은 컬럼은 백업으로 사용하고, 내부에서 로드한 컬럼 우선 사용
- tableManagementApi.getColumnList() 사용하여 동적 로드
이제 원본/대상 테이블 선택 시 해당 테이블의 컬럼 목록이 자동으로 나타남
- TableListConfigPanel: handleNestedChange에서 전체 config 병합 로직 추가
- TableListComponent: checkbox.enabled 및 position 기본값 처리 (undefined시 기본값 사용)
- SelectedItemsDetailInputConfigPanel: handleChange에서 전체 config 병합 로직 추가
- SelectedItemsDetailInputConfigPanel: 원본 데이터 테이블 선택 disabled 조건 제거
- UnifiedPropertiesPanel: allTables 로드 및 ConfigPanel에 전달 추가
문제:
1. table-list 컴포넌트 체크박스 설정 변경 시 다른 설정 초기화
2. selected-items-detail-input 설정 변경 시 컴포넌트 이름 등 다른 속성 손실
3. 원본 데이터 테이블 선택이 비활성화되어 있음
해결:
- 모든 설정 패널에서 부분 업데이트 시 기존 config와 병합하도록 수정
- checkbox 관련 기본값 처리로 기존 컴포넌트 호환성 보장
- allTables를 별도로 로드하여 전체 테이블 목록 제공
- 검색 필터 설정을 화면별로 독립적으로 저장하도록 개선 (screenId 포함)
- FilterPanel, TableSearchWidget, TableListComponent에 화면 ID 기반 localStorage 키 적용
- 동적 모드(사용자 설정)와 고정 모드(디자이너 설정) 2가지 필터 방식 추가
- 고정 모드에서 컬럼 드롭다운 선택 기능 구현
- 컬럼 선택 시 라벨 및 필터 타입 자동 설정
- ConfigPanel 표시 문제 해결 (type='component' 지원)
- UnifiedPropertiesPanel에서 독립 컴포넌트 ConfigPanel 조회 개선
주요 변경:
- 같은 테이블을 사용하는 다른 화면에서 필터 설정이 독립적으로 관리됨
- 고정 모드에서는 설정 버튼이 숨겨지고 지정된 필터만 표시
- 테이블 정보가 있으면 컬럼을 드롭다운으로 선택 가능
- inputType에 따라 filterType 자동 추론 (number, date, select, text)
- buttonActions.ts: formData가 배열인 경우 일반 저장 건너뜀
- SelectedItemsDetailInput이 UPSERT를 완료한 후 일반 저장이 실행되어 null 레코드가 삽입되던 문제 해결
- ScreenModal에서 그룹 레코드를 배열로 전달하는 경우 감지하여 처리
- skipDefaultSave 플래그가 제대로 작동하지 않던 문제 근본 해결
- 필드 자동 매핑 기능 구현
* FieldMapping 타입 추가 (sourceField → targetField)
* applyFieldMappings() 함수로 선택 시 자동 입력
* 여러 필드를 한 번에 자동으로 채움 (거래처 선택 → 주소/전화 자동 입력)
- 값 필드 저장 위치 선택 기능 추가
* ValueFieldStorage 타입 추가 (targetTable, targetColumn)
* 기본값(화면 연결 테이블) 또는 명시적 테이블/컬럼 지정 가능
* 중간 테이블, 이력 테이블 등 다중 테이블 저장 지원
- UI/UX 개선
* 모든 선택 필드를 Combobox 스타일로 통일
* 각 필드 아래 간략한 사용 설명 추가
* 저장 위치 동작 미리보기 박스 추가
- 문서 작성
* 사용_가이드.md 신규 작성 (실전 예제 3개 포함)
* 빠른 시작 가이드, FAQ, 체크리스트 제공
- 하드코딩 제거: 필드명 패턴을 동적으로 처리
- 민감한 필드(id, password, token, company_code)만 제외하고 모두 표시
- 그룹 삭제 기능: 중복 제거 활성화 시 관련된 모든 레코드 삭제
- URL 파라미터 초기화: 모달 닫을 때 자동으로 초기화
- 백엔드: deleteGroupRecords API 추가
- 프론트엔드: dataApi.deleteGroupRecords 클라이언트 추가
- 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: 날짜 필드 타임존 제거 및 포맷팅 개선
문제:
- reference 매핑 시 조인 조건의 소스 필드 값이 undefined
- API 호출 시 filters 파라미터를 백엔드가 인식 못함
해결:
- 컬럼 처리를 2단계로 분리 (source/manual → reference)
- API 파라미터 변경 (filters→search, limit/offset→size/page)
- 응답 경로 수정 (data.data → data.data.data)
결과:
- 외부 테이블 참조 매핑 정상 작동
- 품목 선택 시 customer_item_mapping에서 단가 자동 조회 성공
새로운 기능
- 컬럼별 독립적인 소스 테이블 선택 기능
- SourceColumnSelector, ReferenceColumnSelector 컴포넌트 추가
- 계산 규칙 자동 동기화 로직 (cleanupInitialConfig)
UI/UX 개선
- 컬럼 설정 UI를 세로 레이아웃으로 재구성 (h-10 통일)
- 매핑 타입별 색상 구분 (파란색/보라색/초록색)
- 계산 규칙 섹션 재디자인 (안내 박스, 번호 배지, 빈 상태)
- 현재 설정 시각화 (코드 스타일 표시)
버그 수정
- 계산 규칙 삭제 시 컬럼이 수정 불가능 상태로 남는 문제 해결
- 결과 필드 변경 시 이전 필드의 calculated 속성 제거
- 초기 로드 시 계산 규칙과 컬럼 속성 동기화
개선 사항
- 모든 입력 필드의 높이와 텍스트 크기 일관성 확보
- 섹션별 명확한 제목과 설명 추가
- 접근성 향상 (ARIA 레이블, 포커스 스타일)
문제:
- 메인 화면(거래처 선택) → 첫 번째 모달(품목 선택) → 두 번째 모달(상세 입력)
- selectedRowsData는 바로 이전 화면 데이터만 제공하여 2단계 이전 데이터 접근 불가
- customer_id가 NULL로 저장됨
해결:
- modalDataStore의 전역 레지스트리에서 모든 누적 데이터 접근
- sourceTable에 따라 적절한 데이터 소스 자동 선택
- 거래처 데이터(customer_mng)를 modalDataStore에서 직접 가져옴
기술적 변경:
- ButtonPrimaryComponent: allComponents에서 componentConfigs 수집 및 전달
- ButtonActionContext: componentConfigs 속성 추가
- handleBatchSave: modalDataStore에서 테이블별 데이터 조회
- parentDataMapping 로직: sourceTable 기반 데이터 소스 자동 감지
- 디버깅 로그 강화 (modalDataStore 키, 데이터 소스 추적)
- 여러 테이블(거래처, 품목 등)에서 데이터를 가져와 자동 매핑 가능
- 각 매핑마다 소스 테이블, 원본 필드, 저장 필드를 독립적으로 설정
- 검색 가능한 Combobox로 테이블 및 컬럼 선택 UX 개선
- 소스 테이블 선택 시 해당 테이블의 컬럼 자동 로드
- 라벨, 컬럼명, 데이터 타입으로 검색 가능
- 세로 레이아웃으로 가독성 향상
기술적 변경사항:
- ParentDataMapping 인터페이스 추가 (sourceTable, sourceField, targetField)
- buttonActions.ts의 handleBatchSave에서 소스 테이블 기반 데이터 소스 자동 판단
- tableManagementApi.getColumnList() 사용하여 테이블 컬럼 동적 로드
- Command + Popover 조합으로 검색 가능한 Select 구현
- 각 매핑별 독립적인 컬럼 상태 관리 (mappingSourceColumns)
문제:
- 조건부 컨테이너 내부의 modal-repeater-table 컴포넌트가 데이터 업데이트 불가
- ConditionalSectionViewer가 RealtimePreview에 formData/onFormDataChange 미전달
해결:
- ConditionalSectionViewer.tsx: RealtimePreview에 formData, onFormDataChange props 추가
- DynamicComponentRenderer.tsx: 디버깅 로그 정리
- ScreenModal.tsx: 디버깅 로그 정리
영향:
- 수주 등록 화면 품목 추가 기능 정상 작동
- 조건부 컨테이너 내부 모든 폼 컴포넌트 데이터 바인딩 정상화
Refs: #수주관리 #modal-repeater-table #ConditionalContainer
- ResizableDialog 콘텐츠 영역에 pointer-events 및 z-index 설정 추가
- TextInputComponent를 제어 컴포넌트에서 비제어 컴포넌트로 변경 (value → defaultValue)
- ItemSelectionModal 및 TextInputComponent 디버그 로그 제거
수정 파일:
- frontend/components/ui/resizable-dialog.tsx
- frontend/lib/registry/components/text-input/TextInputComponent.tsx
- frontend/lib/registry/components/modal-repeater-table/ItemSelectionModal.tsx
- uniqueField 값이 undefined일 때 객체 참조 비교로 폴백
- 멀티셀렉트 모드에서 선택/해제 로직 안정화
- 체크박스 클릭 이벤트 전파 개선
- 유효한 컬럼만 렌더링하도록 필터링 추가
- 디버깅을 위한 콘솔 로그 추가
- 선택된 항목의 uniqueField 값 표시
- 선택항목 상세입력 컴포넌트 확장
- 실시간 가격 계산 기능 추가 (할인율/할인금액, 반올림 방식)
- 카테고리 값 기반 연산 매핑 시스템
- 3단계 드릴다운 방식 설정 UI (메뉴 → 카테고리 → 값 매핑)
- 설정 가능한 계산 로직
- autoCalculation 설정으로 계산 필드명 동적 지정
- valueMapping으로 카테고리 코드와 연산 타입 매핑
- 할인 방식: none/rate/amount
- 반올림 방식: none/round/floor/ceil
- 반올림 단위: 1/10/100/1000
- UI 개선
- 입력 필드 가로 배치 (반응형 Grid)
- 카테고리 타입 필드 옵션 로딩 개선
- 계산 결과 필드 자동 표시 및 읽기 전용 처리
- 날짜 입력 필드 네이티브 피커 지원
- API 연동
- 2레벨 메뉴 목록 조회
- 메뉴별 카테고리 컬럼 조회
- 카테고리별 값 목록 조회
- 문서화
- 기간별 단가 설정 가이드 작성
새로운 그룹화 레이아웃 컴포넌트 2종 추가:
- Section Card: 제목+테두리 기반 명확한 섹션 구분
- Section Paper: 배경색 기반 미니멀한 섹션 구분
주요 변경사항:
- 새 컴포넌트 등록 (각 4개 파일: Component, ConfigPanel, Renderer, index)
- UnifiedPropertiesPanel에 인라인 설정 UI 추가 (280줄)
- DetailSettingsPanel ConfigPanel 인터페이스 통일화 (config → componentConfig)
- getComponentConfigPanel에 동적 import 매핑 추가
- 기존 컴포넌트 타입 정리 (autocomplete, entity-search, modal-repeater)
특징:
- shadcn/ui 기반 일관된 디자인 시스템 준수
- 중첩 박스 금지 원칙 적용
- 반응형 지원 (모바일 우선)
- collapsible 기능 지원 (Section Card)
- 필드 그룹을 Collapsible로 변경하여 펼침/접힘 가능
- 항목 표시 설정도 Collapsible로 분리하여 깔끔하게 정리
- 그룹 제목에 displayItems 개수 표시
- 기본적으로 그룹은 펼쳐진 상태, 표시 설정은 접힌 상태
- ChevronDown/ChevronRight 아이콘으로 펼침 상태 표시
- 복잡한 설정을 단계적으로 볼 수 있어 가독성 대폭 향상
- FieldGroup에 displayItems 추가 (그룹별 독립적인 표시 설정)
- SelectedItemsDetailInputConfig에서 전역 displayItems 제거
- renderDisplayItems에 groupId 파라미터 추가하여 그룹별 설정 사용
- 설정 패널에서 그룹별로 displayItems 관리
- 각 그룹마다 다른 표시 형식 가능 (예: 거래처 정보 vs 단가 정보)
- 그룹의 필드만 선택 가능하도록 필터링
- DisplayItem 타입 추가 (icon, field, text, badge)
- 필드별 표시 형식 지원 (text, currency, number, date, badge)
- 빈 값 처리 옵션 추가 (hide, default, blank)
- 기본값 설정 기능 추가
- 스타일 옵션 추가 (굵게, 밑줄, 기울임, 색상)
- renderDisplayItems 헬퍼 함수로 유연한 표시 렌더링
- SelectedItemsDetailInputConfigPanel에 항목 표시 설정 UI 추가
- displayItems가 없으면 기존 방식(모든 필드 나열)으로 폴백
- handleAddGroupEntry: + 추가 버튼 클릭 시 미리 빈 entry를 배열에 추가
- handleFieldChange: 기존 entry 업데이트만 수행 (새로운 entry 추가 로직 제거)
- 이제 첫 글자 입력 시에도 배열 길이가 변하지 않아 포커스가 유지됨
- 입력 중 onFormDataChange 호출 제거하여 불필요한 리렌더링 방지
- 저장 버튼 클릭 시에만 데이터 전달하도록 변경 (beforeFormSave 이벤트)
- handleSave에서 beforeFormSave 이벤트 발생 및 100ms 대기
- 원본 데이터 표시 버그 수정 (modalData 중첩 구조 처리)
- fieldGroups 구조 감지 로직 수정 (details → fieldGroups)
이제 사용자가 타이핑할 때 포커스가 유지됩니다.
- 데이터 구조 변경: ItemData.details → ItemData.fieldGroups (그룹별 관리)
- 각 필드 그룹마다 독립적으로 여러 항목 추가/수정/삭제 가능
- renderFieldsByGroup: 그룹별 입력 항목 목록 + 편집 + 추가 버튼 구현
- renderGridLayout/renderCardLayout: 품목별 그룹 카드 표시로 단순화
- handleFieldChange: groupId 파라미터 추가 (itemId, groupId, entryId, fieldName, value)
- handleAddGroupEntry, handleRemoveGroupEntry, handleEditGroupEntry 핸들러 추가
- buttonActions handleBatchSave: fieldGroups 구조 처리하도록 수정
- 원본 데이터 표시 버그 수정: modalData의 중첩 구조 처리
사용 예:
- 품목 1
- 그룹 1 (거래처 정보): 3개 항목 입력 가능
- 그룹 2 (단가 정보): 5개 항목 입력 가능
- 각 항목 클릭 → 수정 가능
- 저장 시 모든 입력 항목이 개별 레코드로 저장됨