style(rolesList): 다른 메뉴 톤에 맞춰 사이즈/글씨 축소 #12

Merged
hjjeong merged 22 commits from hjjeong into main 2026-05-13 08:23:51 +00:00
Collaborator
No description provided.
hjjeong added 22 commits 2026-05-13 08:23:47 +00:00
본문 텍스트 text-sm → text-xs, 헤더 보조 텍스트 text-xs →
text-[11px], 카드 헤더 p-3 → p-2.5, 좌측 권한 목록 그리드
260px → 220px. Input/Select h-8 → h-7 (메인) / h-7 → h-6 (서브),
메뉴 트리 row py-2 → py-1.5, 트리 들여쓰기 level*20+12 →
level*16+10, chevron h-3.5 → h-3. 4분할 직원 카드 영역
clamp(220, 32vh, 320) → clamp(200, 28vh, 280).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- BatchService: insertBatch/updateBatch 가 body.mappings 받아 replace-all 동기화,
  getBatchInfo 가 batch_mappings 리스트 attach (지금까지는 silently drop)
- batch.xml: getBatchMappingsByConfigId / insertBatchMapping / deleteBatchMappingsByConfigId 신규
- batchExecutionLog.xml / batchManagement.xml: batch_config_id 에 ::varchar 캐스팅,
  오타 'batch_execution_log' → 'batch_execution_logs' 정정
- batchmngList/page.tsx: 같은 batch ID 가 회사 간 중복될 때 React key 충돌 방지
- notes: vexplor_rps → INVYONE 배치 파이프라인 이식 분석 노트

vexplor_rps → INVYONE 파이프라인 이식의 Phase 0.
구체 분해는 notes/hjjeong/2026-05-12-batch-pipeline-current-state.md 참조.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- V021 Flyway: ALTER TABLE BATCH_MAPPINGS ADD COLUMN MAPPING_CONFIG JSONB
- StartupSchemaMigrator: 같은 ALTER 를 idempotent 항목으로 추가 (모든 활성 테넌트 DB 부팅 시 동기화)
- batch.xml: getBatchMappingsByConfigId SELECT 에 MAPPING_CONFIG::TEXT cast,
  insertBatchMapping VALUES 에 #{mapping_config,jdbcType=OTHER}::jsonb
- BatchService: ObjectMapper 주입, parseJsonField/stringifyJsonField 유틸,
  syncMappings 는 INSERT 전 직렬화, attachMappings 는 SELECT 후 Map 으로 역직렬화
- RUN_087_MIGRATION.md: 운영용 마이그레이션 runbook (사전 점검/사후 검증/롤백)

conditional 매핑(when/then/default) 룰을 행 단위 저장하는 컬럼.
direct/fixed 는 NULL. Phase 2~3 에서 프런트/엔진이 이 컬럼을 읽고 쓴다.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- batch.ts: ConditionalRule / ConditionalConfig 타입 추가,
  BatchMapping 에 mapping_type ('direct'|'fixed'|'conditional') + mapping_config 필드
- ConditionalEditor.tsx: 평가 필드 선택 + when/then 룰 add/remove + default 입력 컴포넌트.
  emptyConditionalConfig / normalizeConditionalConfig 헬퍼 동봉. vexplor_rps 1:1 포팅
- batchmngList/edit/[id]/page.tsx:
  · MappingItem.sourceType 에 'conditional' 추가 + conditionalConfig 필드
  · 소스타입 Select 에 "조건 변환" 옵션
  · Load: mapping_type=conditional 인 매핑은 mapping_config JSON 파싱 후 복원
  · Save: sourceType=conditional 매핑은 mapping_config 객체와 함께 전송

저장된 룰: {"rules":[{"when":"1","then":"Y"}],"default":"?"} 형태.
Phase 1 의 BatchService 직렬화 경로로 JSONB 에 저장된다.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
vexplor_rps batchSchedulerService L540~617 의 변환 로직 1:1 이식.
의존성 없는 정적 유틸 — BatchExecutor 가 FROM 읽기 결과를 TO 형태로 변환할 때 사용.

- transformRow: mapping_type 별 분기 (direct/conditional/fixed),
  멀티테넌시용 company_code 자동 주입, 점 표기법 path 평가
- evaluateConditional: ConditionalConfig.rules 의 when/then lookup + default 폴백.
  단순 문자열 동등 비교 (SpEL/JEXL 표현식 평가 안 함)
- getValueByPath: "response.access_token" 같은 중첩 키 지원
- parseConditionalConfig: JSONB 가 Map/String/null 셋 다 가능 — 안전 normalize
- partitionFixed: non-fixed / fixed 매핑 분리 헬퍼

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
vexplor_rps batchSchedulerService.executeBatchMappings 의 1:1 이식.

흐름:
  1. mappings 를 fixed/non-fixed 로 partition
  2. non-fixed 를 (from_connection_type, from_connection_id, from_table_name) 으로 그룹화
  3. 그룹별 FROM 읽기 → MappingTransformer.transformRow → TO 저장
  4. (totalRecords, successRecords, failedRecords) 집계

FROM 소스:
  - internal     : sqlSession.getConnection() 의 동적 SELECT (식별자 화이트리스트 escape)
  - external_db  : ExternalDbConnectionService.executeQuery (SELECT-only)
  - restapi      : ExternalRestApiConnectionService.fetchData (등록된 연결 + dataArrayPath)

TO 대상:
  - internal     : INSERT / UPSERT(save_mode + conflict_key, ON CONFLICT DO UPDATE/NOTHING)
                   updated_date 컬럼 있으면 자동으로 NOW() 갱신
  - restapi      : 행 단위 POST/PUT/DELETE — testConnection 으로 호출
  - external_db  : 미지원 (보안 정책: ExternalDbConnectionService 가 SELECT-only)

vexplor_rps 대비 단순화 항목 (필요 시 후속 Phase 로 분리):
  - to_api_body 템플릿 기반 일괄 전송
  - URL_PATH_PARAM 컬럼 처리
  - auth_tokens 자동 조회 (inline-mode REST API: from_connection_id 없이 from_api_url 직접 호출)
  - row_filter_config

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
기존 stub(0 만 리턴) 였던 BatchManagementService.executeBatchConfig 를 실제 ETL 실행 + 로그 기록 흐름으로 교체.

흐름:
  1. RUNNING 상태로 batch_execution_logs INSERT (도중 비정상 종료 추적용)
  2. BatchExecutor.execute(batchConfig) 호출
     - 정상: failedRecords > 0 면 PARTIAL/FAILED, 아니면 SUCCESS
     - 예외: FAILED 로 마킹, error_message 기록
  3. 로그 UPDATE — execution_status, end_time, duration_ms, total/success/failed_records, error_message
  4. controller 응답에 execution_status + error_message 동봉

server_name 은 InetAddress.getLocalHost(), process_id 는 ProcessHandle.current().pid().

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
검증 전 선제 보강 2건.

1) BatchExecutor.writeTo 의 @Transactional 제거
   - 같은 클래스 내부 호출(execute → writeTo)이라 Spring AOP 가 우회되어 어차피 안 걸림
   - batch 의 정상 동작은 row 단위 독립 commit (일부 실패해도 다른 row 는 살아야 함).
     vexplor_rps 도 동일 패턴

2) BatchManagementService.executeBatchConfig 의 end_time 을 Timestamp 객체로 직접 전달
   - 기존: Timestamp.toString() 으로 변환 후 #{end_time}::timestamp 캐스트
   - 신규: Timestamp 객체 그대로 → PostgreSQL JDBC 가 timestamp 자동 변환,
     mapper 의 ::timestamp 캐스트는 noop

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
vexplor_rps 알고리즘 1:1 이식이 정상 동작하는지 검증.

검증 항목:
- evaluateConditional: 단순 매칭 / null cfg / 빈 rules
- parseConditionalConfig: Map / String(JSON) / null / 손상 JSON 모두 안전
- getValueByPath: 단순 키 / 중첩 경로 / 없는 경로 / null obj
- partitionFixed: fixed / non-fixed 분리
- transformRow: direct / conditional("1"→"Y") / conditional default 폴백 /
  fixed 적용 / 점 표기법 ("user.profile.name") / company_code 자동주입 조건

18 tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
운영 DB 검증 결과 batch_execution_logs.duration_ms / total_records /
success_records / failed_records 가 모두 character varying 으로 정의됨
(V001 legacy 마이그레이션 흔적). PgJDBC 가 Long/Integer 를 VARCHAR 컬럼에
자동 변환하지 못할 위험이 있어 명시적으로 String.valueOf 로 변환 후 전달.

mapper 의 COALESCE default 가 '0' (문자열) 인 점과도 일관.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
문제 1: 배치 수정/생성 탭이 빈 화면
- Tab type 정의는 admin_url (snake_case) 인데 호출자가 adminUrl (camelCase)
  로 전달 → TabPageRenderer 매칭 실패 → null 렌더 → 빈 화면
- 콘솔 경고: "[TabPageRenderer] 렌더링 불가 — 매칭 조건 없음" (TabContent.tsx:268)
- 11곳 (4파일) 의 adminUrl → admin_url 정정

문제 2: 리스트/편집 본문이 좁은 가운데 컬럼에 박혀있음
- batchmngList/page.tsx: max-w-[720px] → w-full
- batchmngList/edit/[id]/page.tsx: max-w-[640px] → w-full

해당 파일:
- batchmngList/page.tsx
- batchmngList/edit/[id]/page.tsx
- batchmngList/create/page.tsx
- batch-management-new/page.tsx

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
vexplor_rps 와 비교 시 batch-management-new 에 조건 변환 모드가 빠져있어
batchmngList/edit/[id] 와 같은 패턴으로 정렬.

- MappingItem.sourceType 에 'conditional' 추가 + conditionalConfig 필드
- ConditionalConfig / ConditionalEditor / emptyConditionalConfig import
- Select 옵션에 "조건 변환" 추가
- conditional 일 때 ConditionalEditor 렌더
- validMappings 필터: conditional 은 평가 필드(apiField) + 룰 또는 default 필요
- apiMappings 변환: mapping_type='conditional' + mapping_config 객체 전송

note: 이 페이지는 신규 생성 전용이라 load(편집 복원) 로직은 없음.
편집은 batchmngList/edit/[id]/page.tsx 가 처리.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
vexplor_rps batch-management-new 의 applyRegisteredRestApi 핵심 흐름을 INVYONE 에
이식. 등록된 REST API 연결 선택 시:
  1. 폼(URL/엔드포인트/메서드/Body/인증 토큰) 자동 채움
  2. ExternalRestApiConnectionAPI.testConnectionById 로 자동 API 호출
  3. 응답 안에서 배열 자동 탐색 (depth ≤ 4)
  4. fromApiFields / fromApiData 채움 → 매핑 드롭다운에 필드 즉시 노출

UI: FROM 카드 최상단에 "🔗 등록된 연결" 셀렉터 추가.
로딩 중에는 셀렉터 라벨 옆에 스피너, 에러 시 destructive 톤 메시지.

vexplor_rps 와 다르게 제외한 부분:
  - Amaranth/Wehago 회사 전용 프리셋 (AMARANTH_TARGET_PRESETS) — 6종 ERP 동기화
    배치 전용 하드코딩이라 INVYONE 일반 사용자에겐 의미 없음. Phase 6 후속에서
    검토할 별도 영역.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
이전: API 서버 URL / 인증 토큰 / 엔드포인트 / 메서드 / 데이터 배열 경로가 각각 단독 행
신규: 의미 단위로 그룹화 + 컴팩트화

- API 서버 URL (3fr) + HTTP 메서드 (1fr) 한 행
- 엔드포인트 + 데이터 배열 경로 50:50 한 행
- 인증 토큰: 라디오 → 세그먼티드 토글 버튼 그룹 + 입력을 한 행에 압축
- Label text-xs / Input h-9 text-sm 로 컴팩트 통일

vexplor_rps batch-management-new (L1417 부근) 의 컴팩트 레이아웃 패턴 참고.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0bba1836 에서 batchmngList/page.tsx 와 edit/[id]/page.tsx 두 곳만 max-w 제거했고
batch-management-new 의 root mx-auto max-w-5xl 가 남아있어 가운데 1024px 컬럼으로
본문이 박혀있었음. 좌우 패널이 sparse 하게 보이는 원인.

mx-auto max-w-5xl → w-full

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
사용자 보고: 기본 정보 카드가 별도 영역을 차지해서 한 화면에 매핑 영역(중요) 까지
스크롤 없이 안 들어옴. vexplor_rps 스크린샷처럼 상단을 한 줄로 컴팩트하게.

변경:
- 배치 타입 토글 grid + 기본 정보 카드를 단일 grid 로 통합
- xl(1280px+): grid-cols-[minmax(28rem,1.4fr)_1fr_1fr_1.5fr]
  → 모드토글그룹 + 배치명 + 스케줄 + 설명 한 줄
- xl 미만: grid-cols-1 stack
- 모드 토글 카드 컴팩트화: p-3 / h-9 아이콘 / text-[10px] description
- 설명: Textarea → Input (단일 행) 로 축소. 긴 설명은 어차피 메모 용도라 한 줄이면 충분.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
외부 커넥션 관리에서 연결 테스트 시 500 발생.
원인: 운영 DB 의 external_rest_api_connections.id 가 V001 legacy 마이그레이션으로
character varying 인데 mapper 의 WHERE ID = #{id} 가 controller 의 int id 를
그대로 받아 PgJDBC 가 보내는 BIND 가 정수.
PostgreSQL 이 "operator does not exist: character varying = integer" 거부 →
SQLException → ApiResponse 500 ("서버 내부 오류").

수정: 4곳 모두 #{id}::varchar 캐스팅 추가.
  - getExternalRestApiConnectionInfo (SELECT)
  - updateExternalRestApiConnection (UPDATE)
  - deleteExternalRestApiConnection (DELETE)
  - updateExternalRestApiConnectionTestResult (UPDATE)

배치 작업의 batch_config_id 패턴과 동일. 같은 V001 영향을 받은 다른 mapper
(externalDbConnection.xml / externalCallConfig.xml / booking.xml / delivery.xml /
multiConnection.xml / taxInvoice.xml 등) 도 같은 수정이 필요할 가능성 — 별도 작업으로 분리.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
이전: 페이지 진입 시 항상 펼쳐져 큰 공간 차지 → 매핑 영역이 화면 밖으로 밀림
신규: <details> + summary 로 기본 접힘. 클릭 시만 펼침. 토글 아이콘 함께.

특정 사용자/조건으로 API 조회할 때만 쓰는 옵션이라 기본 접힘이 자연스러움.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
데이터 불러오고 매핑하기 클릭 시 400 발생.
원인: 프론트는 camelCase (apiUrl/endpoint/method/apiKey/dataArrayPath/paramType/...)
로 body 를 보내는데 백엔드는 snake_case (api_url/endpoint/method/api_key/...) 키로 읽음.
다른 service 진입점 (updateBatchConfig / executeBatchConfig 등) 은 convertCamelToSnake
를 호출해서 자동 변환하는데 previewRestApiData 만 빠져있어 isBlank(apiUrl)=true 가
되며 IllegalArgumentException → 400.

수정: previewRestApiData 진입부에 convertCamelToSnake(body) 한 줄 추가.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
배치 생성 흐름 검증 중 발견된 4가지 이슈 일괄 정정.

1) BatchManagementService.previewRestApiData — camelCase 키 명시 remap
   직전 커밋(b752de23)에서 convertCamelToSnake() 호출 추가했지만 그 함수의 실제 구현이
   batch_configs 전용 snake→snake remap 이라 사실상 no-op. 프론트의 apiUrl 등 camelCase
   가 변환되지 않아 isBlank(api_url)=true → 400.
   → previewRestApiData 진입부에 직접 remap (apiUrl/apiKey/requestBody/dataArrayPath/
     paramType/paramName/paramValue/paramSource/authServiceName 9개 키).

2) batchManagement.ts.previewRestApiData — 응답 totalCount 정규화
   백엔드는 total_count (snake_case) 로 응답하는데 프론트는 result.totalCount 로 읽음.
   토스트가 "2개 필드, undefined개 레코드" 로 표시됨.
   → 응답 normalize: total_count ?? totalCount ?? 0.

3) batch-management-new/page.tsx — root h-full overflow-y-auto
   페이지 root 가 overflow 처리가 없어 FROM/TO 카드 아래의 매핑 카드가 탭 컨테이너
   밖으로 잘려 사용자가 못 봄.
   → root div 에 h-full overflow-y-auto 추가.

4) RestApiToDbMappingCard — v5 컨벤션에 맞춘 컴팩트화
   다른 메뉴들과 톤 통일. CardHeader 패딩 축소, 폰트 size 일괄 다운,
   행 padding p-3 → p-2, Select/Input h-9 → h-7 text-xs, 순서 원형 h-6 → h-5,
   카드 내부 height 360 → 300px, 매핑 추가 버튼/삭제 버튼 컴팩트.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
새 배치 (REST API → DB) 진입 시 매번 사용자가 "데이터베이스 커넥션 선택" 셀렉트에서
"내부 DB" 를 직접 골라야 했음. 대부분의 배치가 internal 적재라 디폴트 채움이 자연스러움.

1) TO DB 자동 선택
   useEffect 로 batchType === "restapi-to-db" + connections 로드 + toConnection 비어있음
   조건 만족 시 handleToConnectionChange("internal") 자동 호출. 사용자가 외부 DB 로 변경하면
   toConnection != null 이 되어 더 이상 자동 동작 안 함.

2) Select controlled 화
   DB 커넥션/테이블 Select 가 value prop 없는 uncontrolled 상태였음.
   setToConnection/setToTable state 가 바뀌어도 Select UI 가 placeholder 그대로 →
   programmatic 자동 선택이 시각적으로 반영 안 됨.
   → value prop 추가:
     - DB 커넥션: toConnection.type === "internal" ? "internal" : String(toConnection.id)
     - 테이블: toTable

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hjjeong merged commit 0328f618b9 into main 2026-05-13 08:23:51 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gbpark/invyone#12