직전 Phase 1 의 후속 폴리시.
신규 백엔드
- crosstenant/CrossTenantDeptController.java
GET /api/admin/cross-tenant/departments?company_code=TEST02
단일 모드 GET /admin/departments 와 응답 형태 동일. company_code query param
으로 명시된 회사 DB 컨텍스트로 임시 전환해서 부서 트리 반환.
버그 수정: 메타 DB DEPT_INFO 시드 (qnc/COMPANY_7 등 다른 회사 부서) 가
TEST02 선택 시에도 dropdown 에 섞여 보이던 문제 해결.
프론트
- lib/api/user.ts — getDepartmentList(companyCode) 가 isCrossTenantMode() 면
/admin/cross-tenant/departments?company_code= 호출.
cross-tenant 모드 + companyCode 미지정 → 빈 배열 반환 (회사 안 골랐는데
메타 부서 보여주는 것 방지).
UserFormModal
- 회사 dropdown 을 폼 가장 위로 이동 — 사용자 ID 중복확인·부서 선택이
모두 회사에 의존하므로 자연스러운 입력 순서
- SUPER_ADMIN 인데 회사 미선택 상태에선 사용자 ID input + 중복확인 버튼
disable + placeholder "회사 먼저 선택"
- checkUserIdDuplicate 가드: 회사 미선택이면 "회사를 먼저 선택해주세요"
(백엔드의 400 "company_code 가 비어있음" 보다 친절)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
지금까지 cross-tenant 는 READ 전용. admin 도메인에서 사용자 등록하면
JWT.company_code='*' 가 그대로 박혀 메타 DB 에 INSERT 되던 버그 해결.
이제 SUPER_ADMIN 이 폼의 "회사" 드롭다운에서 TEST01/TEST02 등 선택하면
그 회사 DB 에 정확히 INSERT.
신규 백엔드
- crosstenant/CrossTenantExecutor.java — 회사 컨텍스트 임시 전환 헬퍼
(company_code → db_name → ensureTenantPool → set → run → restore)
- crosstenant/CrossTenantUserController.java — /api/admin/cross-tenant/users
9개 endpoint (POST/PUT/DELETE/PATCH/with-dept/check-duplicate/단건/이력)
- mapper/provisioning.xml — resolveDbNameByCompanyCode (active 회사만)
기존 단일 회사 모드 (POST /admin/users 등) 무수정 — 회사 도메인
컨텍스트에서 회귀 0.
프론트
- lib/api/user.ts — createUser/updateUser/updateUserStatus/checkDuplicateUserId/
saveUserWithDept 가 isCrossTenantMode() 면 새 endpoint + body.company_code 로 분기
- UserFormModal — checkDuplicateId 호출 시 formData.company_code 같이 전달
- useUserManagement — status toggle 시 row 의 company_code 같이 전달
검증 (curl, SUPER_ADMIN 토큰):
- 토큰 없음 → 403 super_admin_required
- company_code 없음 → 400 "company_code 가 비어있음"
- 잘못된 company_code → 400 "등록되지 않았거나 비활성 회사"
- check-duplicate: TEST01.test02_admin → not_dup, TEST02.test02_admin → dup ✓
- POST 사용자 → TEST02 USER_INFO +1, TEST01·메타 격리 ✓
- /users fan-out: by={'*':8, 'TEST01':1, 'TEST02':2}, hjtest_ct_001 in TEST02만 ✓
- DELETE → status=inactive (soft) ✓
미구현 (Phase 1 후속):
- 부서 dropdown (cross-tenant department endpoint 별도 필요)
- 비밀번호 초기화 모달의 cross-tenant 분기 (UserPasswordResetModal)
- Phase 2 권한관리 (별도 커밋)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SUPER_ADMIN cross-tenant 모드에서 회사당 cap 200 에 걸리거나 한 회사
조회 실패 시 화면 상단에 안내 배너 노출. 아무 메타 없으면 자리 안 잡음.
신규
- components/common/CrossTenantBanner.tsx — amber(truncated) + red(failed)
v5 토큰 (surface-solid + glow-sm) 기반 솔리드 배너. blur 안 씀
API 클라이언트 4개에 cross_tenant_meta 노출
- lib/api/user.ts — userAPI.getList 응답에 cross_tenant_meta 추가
- lib/api/role.ts — roleAPI.getList 동일
- lib/api/batch.ts — BatchAPI.getBatchConfigs 동일
- lib/api/multilang.ts — getLangKeys 동일 (i18nList 페이지는 아직 직접
호출 패턴이라 자동 적용 X — 후속에서 페이지를 getLangKeys 로 통일하면 동작)
페이지 마운트 (3개)
- userMng/userMngList — useUserManagement hook 에 crossTenantMeta state 추가
- userMng/rolesList — loadRoleGroups 에서 메타 set
- automaticMng/batchmngList — loadBatchConfigs 에서 메타 set
- systemMng/i18nList — 스킵 (cross-tenant aggregation 미적용 상태, 별도 작업)
설계서 §11 검증 (직전 §11.2 부분 실패 시뮬) 결과: failed 배너가
header X-CrossTenant-Failed 와 동일 정보로 화면에 노출됨.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SUPER_ADMIN 토큰(company_code=*)이면 등록 회사들 DB 를 순회해 결과를
집계해 돌려주는 CrossTenantAggregator/Controller 추가. 사용자/권한그룹/
배치/다국어 키 4개 도메인의 list API 가 cross-tenant 모드 지원.
UserTable + ResponsiveDataView 에 compact/scrollContainer prop 추가.
페이지 헤더/툴바/페이지네이션은 고정, 테이블만 자체 스크롤.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 여러 문서의 내용을 업데이트하여 최신 정보를 반영하였습니다.
- 컴포넌트 개발 가이드와 관련된 문서의 목차를 재구성하고, V2 및 Zod 레이아웃 시스템에 대한 내용을 추가하였습니다.
- 화면 컴포넌트 개발 가이드를 개선하여 핵심 원칙과 패턴을 명확히 설명하였습니다.
- 불필요한 문서 및 가이드를 삭제하고, 통합된 가이드를 통해 개발자들이 쉽게 참고할 수 있도록 하였습니다.
- CustomApiSaveConfig 타입 정의 (apiType, mainDeptFields, subDeptFields)
- saveWithCustomApi() 함수 추가로 테이블 직접 저장 대신 전용 API 호출
- adminController에 saveUserWithDept(), getUserWithDept() API 추가
- user_info + user_dept 트랜잭션 저장, 메인 부서 변경 시 자동 겸직 전환
- ConfigPanel에 전용 API 저장 설정 UI 추가
- SplitPanelLayout2: getColumnValue()로 조인 테이블 컬럼 값 추출 개선
- 검색 컬럼 선택 시 표시 컬럼 기반으로 변경