diff --git a/INDEX.md b/INDEX.md index 883cd84..8ada949 100644 --- a/INDEX.md +++ b/INDEX.md @@ -46,6 +46,8 @@ | 경조사비 서비스 이관 | `dev/dev_plans/condolence-expense-service-plan.md` | MNG 경조사비 → API+React 서비스 이관 기획 | | 계정별원장 서비스 이관 | `dev/dev_plans/account-ledger-service-migration-plan.md` | MNG 계정별원장 → API+React 서비스 이관 기획 (API 완료) | | 손익계산서 서비스 이관 | `dev/dev_plans/income-statement-service-migration-plan.md` | MNG 손익계산서 → API+React 서비스 이관 기획 (API 완료) | +| 조직도 서비스 이관 | `dev/dev_plans/org-chart-service-migration-plan.md` | MNG 조직도 → API+React 서비스 이관 기획 (API 완료) | +| 조직도 FE 가이드 | `frontend/v1/org-chart-fe-guide.md` | 조직도 React 구현 가이드 (API 스펙, UI 설계, 드래그앤드롭) | | 서버 운영 | `dev/deploys/ops-manual/README.md` | 서버 운영 매뉴얼 | | 서버 접근/백업 | `system/server-access-management.md` | 계정, 권한, 백업, 리플리케이션 | | 이관 작업 | `system/migration-status.md` | MNG→API+React 이관 현황, 우선순위, 로드맵 | diff --git a/dev/dev_plans/org-chart-service-migration-plan.md b/dev/dev_plans/org-chart-service-migration-plan.md new file mode 100644 index 0000000..3333253 --- /dev/null +++ b/dev/dev_plans/org-chart-service-migration-plan.md @@ -0,0 +1,194 @@ +# 조직도 서비스 이관 기획서 + +> **작성일**: 2026-03-22 +> **상태**: API 구현 완료, React 문서 제공 +> **대상**: MNG → API + React (서비스 이관) +> **위치**: 서비스 > 인사관리 > 조직도 + +--- + +## 1. 개요 + +### 1.1 목적 + +MNG 백오피스의 조직도 관리 기능을 서비스(API + React)로 이관한다. +기존 MNG의 Alpine.js + SortableJS 기반 UI를 REST API + React 구조로 재구현하며, +멀티테넌트 격리를 API 레이어에서 강화한다. + +### 1.2 배경 + +- MNG `R&D > 조직도 관리` 메뉴로 v1.0 구현 완료 (2026-03-06) +- 부서 CRUD + 트리 + 사용자 배치 API는 이미 존재 (`DepartmentController`) +- 조직도 전용 기능(직원 일괄 이동, 부서 순서/계층 변경, 숨기기)이 API에 없었음 +- MNG는 `session('selected_tenant_id')` 수동 격리 → API는 `BelongsToTenant` 글로벌 스코프 + +### 1.3 이관 범위 + +| 구분 | MNG (현재) | API + React (이관 후) | +|------|-----------|---------------------| +| **백엔드** | RdController 직접 로직 | OrgChartService + OrgChartController + FormRequest | +| **프론트** | Alpine.js + SortableJS | React (Next.js) + @dnd-kit | +| **인증** | 세션 기반 | Bearer 토큰 + `BelongsToTenant` | +| **데이터** | `withoutGlobalScopes()` 수동 | 글로벌 스코프 자동 격리 | +| **API 문서** | 없음 | Swagger | + +--- + +## 2. 현재 MNG 기능 분석 + +### 2.1 MNG 엔드포인트 (RdController) + +| Route | Method | 설명 | +|-------|--------|------| +| `/rd/org-chart` | GET | 조직도 페이지 (트리+직원+회사정보) | +| `/rd/org-chart/assign` | POST | 직원 부서 배치 | +| `/rd/org-chart/unassign` | POST | 직원 미배치 처리 | +| `/rd/org-chart/reorder` | POST | 직원 일괄 이동 | +| `/rd/org-chart/reorder-depts` | POST | 부서 트리 순서/계층 변경 | +| `/rd/org-chart/toggle-hide` | POST | 부서 숨기기/표시 토글 | + +### 2.2 데이터 모델 + +- **부서**: `departments` 테이블 (자기참조 `parent_id`, `sort_order`, `options.orgchart_hidden`) +- **직원**: `tenant_user_profiles` 테이블 (`department_id`로 소속 관리) +- **회사**: `tenants` 테이블 (`company_name`, `ceo_name`) + +### 2.3 기존 API 구현 (DepartmentController) + +| 기능 | 상태 | +|------|:----:| +| 부서 CRUD | ✅ | +| 부서 트리 조회 (`/tree`) | ✅ | +| 사용자 배정 (`attachUser/detachUser`) | ✅ (department_user 피벗) | +| 조직도 전용 기능 | ❌ → **이번 이관 대상** | + +--- + +## 3. API 설계 + +### 3.1 신규 엔드포인트 (`/v1/org-chart`) + +| Method | Path | 설명 | +|--------|------|------| +| GET | `/v1/org-chart` | 조직도 전체 (트리+직원+통계) | +| GET | `/v1/org-chart/stats` | 통계 (전체/배치/미배치) | +| GET | `/v1/org-chart/unassigned?q=` | 미배치 직원 | +| POST | `/v1/org-chart/assign` | 직원 부서 배치 | +| POST | `/v1/org-chart/unassign` | 직원 미배치 처리 | +| PUT | `/v1/org-chart/reorder-employees` | 직원 일괄 이동 | +| PUT | `/v1/org-chart/reorder-departments` | 부서 순서/계층 변경 | +| PATCH | `/v1/org-chart/departments/{id}/toggle-hide` | 부서 숨기기 토글 | + +### 3.2 주요 응답 구조 + +**GET `/v1/org-chart`**: +```json +{ + "company": { "name": "...", "ceo_name": "..." }, + "departments": [{ "id": 1, "name": "...", "children": [...], "employees": [...] }], + "hidden_departments": [{ "id": 5, "name": "..." }], + "unassigned": [{ "id": 12, "display_name": "...", "position_label": "..." }], + "stats": { "total": 50, "assigned": 42, "unassigned": 8 } +} +``` + +--- + +## 4. API 구현 상세 + +### 4.1 파일 구조 + +``` +api/ +├── app/Http/Controllers/Api/V1/OrgChartController.php (신규) +├── app/Http/Requests/OrgChart/ +│ ├── AssignRequest.php +│ ├── UnassignRequest.php +│ ├── ReorderEmployeesRequest.php +│ ├── ReorderDepartmentsRequest.php +│ └── ToggleHideRequest.php +├── app/Services/OrgChartService.php (신규) +├── app/Models/Tenants/Department.php (수정: options cast) +├── app/Swagger/v1/OrgChartApi.php (신규) +└── routes/api/v1/hr.php (수정: 라우트 추가) +``` + +### 4.2 설계 결정 + +- **별도 OrgChartController/Service 분리**: 기존 DepartmentController(13개 메서드)와 관심사 분리 +- **TenantUserProfile.department_id 기반**: MNG와 동일하게 직원의 소속 부서 필드 직접 사용 +- **순환 참조 방지**: `reorderDepartments`에서 `parent_id` 변경 시 자손→조상 순환 검증 + +--- + +## 5. React 구현 (FE 가이드) + +> **별도 문서**: `docs/frontend/v1/org-chart-fe-guide.md` + +--- + +## 6. 인증 및 권한 + +### 6.1 React → API + +- `authenticatedFetch` 사용 (HttpOnly 쿠키 기반 Sanctum) +- `auth:sanctum` 미들웨어 자동 적용 (HR 라우트 그룹) + +### 6.2 MNG → API (해당 없음) + +- MNG는 기존 코드를 유지하고 자체 DB 직접 접근 +- 이관 완료 후 MNG 메뉴를 숨기고 React로 안내 + +--- + +## 7. 구현 순서 + +| Phase | 작업 | 상태 | +|-------|------|:----:| +| 1 | Department 모델 `options` cast 추가 | ✅ 완료 | +| 2 | OrgChartService + FormRequest 5개 생성 | ✅ 완료 | +| 3 | OrgChartController + Route 등록 | ✅ 완료 | +| 4 | Swagger 문서 (OrgChartApi.php) | ✅ 완료 | +| 5 | React FE 구현 | 📋 문서 제공 | +| 6 | 통합 테스트 + MNG 메뉴 전환 | ⏳ 대기 | + +--- + +## 8. MNG 기존 코드 처리 + +- MNG `RdController.orgChart*` 메서드 → **유지** (삭제하지 않음) +- React 구현 완료 후 MNG 메뉴에서 조직도를 비활성화하고 서비스로 안내 + +--- + +## 9. 체크리스트 + +### API +- [x] `OrgChartService` 8개 메서드 구현 +- [x] `OrgChartController` FormRequest DI 적용 +- [x] 순환 참조 방지 검증 (reorderDepartments) +- [x] `Department` 모델 `options` cast 추가 +- [x] Swagger 문서 생성 확인 +- [x] Pint 포맷팅 통과 + +### React +- [ ] `org-chart-fe-guide.md` 기반 구현 +- [ ] 드래그 앤 드롭 (부서/직원) +- [ ] 미배치 직원 패널 +- [ ] 부서 숨기기/복원 + +--- + +## 관련 문서 + +| 문서 | 경로 | +|------|------| +| 조직도 시스템 (MNG) | `projects/org-chart/README.md` | +| 부서 API 규칙 | `rules/department-tree-api.md` | +| 직원 API 규칙 | `rules/employee-api.md` | +| React FE 가이드 | `frontend/v1/org-chart-fe-guide.md` | +| 이관 현황 | `system/migration-status.md` | + +--- + +**최종 업데이트**: 2026-03-22 diff --git a/frontend/v1/org-chart-fe-guide.md b/frontend/v1/org-chart-fe-guide.md new file mode 100644 index 0000000..2d1043a --- /dev/null +++ b/frontend/v1/org-chart-fe-guide.md @@ -0,0 +1,231 @@ +# 조직도 React 프론트엔드 구현 가이드 + +> **작성일**: 2026-03-22 +> **상태**: API 완료, FE 구현 대기 +> **대상**: React (Next.js) 프론트엔드 개발자 +> **페이지 URL**: `/hr/org-chart` + +--- + +## 1. 개요 + +MNG의 조직도 관리 기능을 React로 재구현한다. API는 이미 구현 완료(`/v1/org-chart`). +드래그 앤 드롭으로 직원 배치, 부서 순서/계층 변경, 부서 숨기기 기능을 제공한다. + +--- + +## 2. API 엔드포인트 + +| Method | Path | 설명 | +|--------|------|------| +| GET | `/v1/org-chart` | 조직도 전체 (트리+직원+통계) | +| GET | `/v1/org-chart/stats` | 통계 (전체/배치/미배치) | +| GET | `/v1/org-chart/unassigned?q=` | 미배치 직원 검색 | +| POST | `/v1/org-chart/assign` | 직원 → 부서 배치 | +| POST | `/v1/org-chart/unassign` | 직원 미배치 처리 | +| PUT | `/v1/org-chart/reorder-employees` | 직원 일괄 이동 | +| PUT | `/v1/org-chart/reorder-departments` | 부서 순서/계층 변경 | +| PATCH | `/v1/org-chart/departments/{id}/toggle-hide` | 부서 숨기기 토글 | + +> Swagger UI: `/api-docs` → "OrgChart" 태그 참조 + +--- + +## 3. 응답 구조 + +### 3.1 GET `/v1/org-chart` + +```json +{ + "success": true, + "data": { + "company": { + "name": "주일산업", + "ceo_name": "홍길동" + }, + "departments": [ + { + "id": 1, + "name": "경영지원팀", + "code": "MGT", + "parent_id": null, + "sort_order": 1, + "is_active": true, + "orgchart_hidden": false, + "children": [], + "employees": [ + { + "id": 5, + "user_id": 3, + "department_id": 1, + "display_name": "김과장", + "position_label": "과장" + } + ] + } + ], + "hidden_departments": [ + { "id": 5, "name": "해외사업팀", "code": "OVS" } + ], + "unassigned": [ + { "id": 12, "user_id": 8, "display_name": "이사원", "position_label": "사원" } + ], + "stats": { + "total": 50, + "assigned": 42, + "unassigned": 8 + } + } +} +``` + +### 3.2 요청 바디 예시 + +**POST `/v1/org-chart/assign`**: +```json +{ "employee_id": 5, "department_id": 3 } +``` + +**PUT `/v1/org-chart/reorder-employees`**: +```json +{ + "moves": [ + { "employee_id": 5, "department_id": 3 }, + { "employee_id": 6, "department_id": null } + ] +} +``` + +**PUT `/v1/org-chart/reorder-departments`**: +```json +{ + "orders": [ + { "id": 1, "parent_id": null, "sort_order": 0 }, + { "id": 2, "parent_id": 1, "sort_order": 1 } + ] +} +``` + +**PATCH `/v1/org-chart/departments/{id}/toggle-hide`**: +```json +{ "hidden": true } +``` + +--- + +## 4. 컴포넌트 구조 (제안) + +``` +src/ +├── app/[locale]/(protected)/hr/ +│ └── org-chart/ +│ └── page.tsx ← 페이지 진입점 +├── components/hr/org-chart/ +│ ├── index.tsx ← 메인 컨테이너 +│ ├── OrgChartTree.tsx ← 부서 트리 렌더링 +│ ├── DepartmentNode.tsx ← 부서 카드 (접기/펼치기, 숨기기) +│ ├── EmployeeCard.tsx ← 직원 카드 (드래그 가능) +│ ├── UnassignedPanel.tsx ← 미배치 직원 패널 (검색 포함) +│ ├── HiddenDepartmentsPanel.tsx ← 숨겨진 부서 복원 패널 +│ ├── OrgChartStats.tsx ← 상단 통계 표시 +│ ├── CompanyHeader.tsx ← 회사명 + CEO 표시 +│ ├── actions.ts ← Server Actions +│ └── types.ts ← TypeScript 타입 정의 +``` + +--- + +## 5. UI 구성 (MNG 참조) + +MNG 조직도 UI: `mng/resources/views/rd/org-chart.blade.php` + +### 5.1 화면 레이아웃 + +``` +┌───────────────────────────────────────────────────────┐ +│ 조직도 관리 전체 50명 | 배치 42명 | 미배치 8명 │ ← 헤더 + 통계 +├───────────────────────────────────────────────────────┤ +│ ┌─미배치 직원──────┐ ┌─조직도 트리───────────────────┐ │ +│ │ 🔍 검색 │ │ ┌──────────────────────────┐ │ │ +│ │ │ │ │ 회사명 │ │ │ +│ │ ▪ 이사원 (사원) │ │ │ CEO: 홍길동 │ │ │ +│ │ ▪ 박대리 (대리) │ │ └──────────────────────────┘ │ │ +│ │ │ │ │ │ +│ │ (드래그 가능) │ │ ┌─경영지원팀 (MGT)──────────┐ │ │ +│ │ │ │ │ 김과장 (과장) [미배치 ✕] │ │ │ +│ │ │ │ │ 최대리 (대리) [미배치 ✕] │ │ │ +│ │ │ │ └──────────────────────────┘ │ │ +│ │ │ │ ┌─생산팀 (PRD)───────────────┐ │ │ +│ │ │ │ │ ... │ │ │ +│ │ │ │ └────────────────────────────┘ │ │ +│ └──────────────────┘ └──────────────────────────────┘ │ +│ ┌─숨겨진 부서──────────────────────────────────────────┐ │ +│ │ 해외사업팀 [복원] 영업3팀 [복원] │ │ +│ └──────────────────────────────────────────────────────┘ │ +└───────────────────────────────────────────────────────┘ +``` + +### 5.2 인터랙션 + +| 동작 | 효과 | API 호출 | +|------|------|---------| +| 미배치 직원 → 부서 드래그 | 직원 배치 | `POST /assign` 또는 `PUT /reorder-employees` | +| 부서 내 직원 → 미배치 패널 드래그 | 직원 미배치 | `POST /unassign` | +| 부서 카드 드래그 (순서 변경) | 부서 순서/계층 변경 | `PUT /reorder-departments` | +| 부서 카드 더블클릭 → "숨기기" | 조직도에서 숨김 | `PATCH /departments/{id}/toggle-hide` | +| 숨겨진 부서 "복원" 클릭 | 조직도에 다시 표시 | `PATCH /departments/{id}/toggle-hide` | + +--- + +## 6. 드래그 앤 드롭 구현 + +### 6.1 라이브러리 권장 + +- **@dnd-kit**: React 전용, 접근성 지원, 트리 구조 지원 +- **대안**: react-beautiful-dnd (유지보수 중단), react-dnd + +### 6.2 핵심 구현 포인트 + +1. **직원 드래그**: 미배치 패널 ↔ 부서 카드 간 이동 +2. **부서 드래그**: 부서 카드 간 순서/계층 변경 (parent_id + sort_order) +3. **순환 참조 방지**: 부서를 자기 자손에 드롭하면 무시 (API에서도 검증하지만 UI에서도 방지) +4. **Optimistic Update**: 드래그 완료 시 로컬 상태 즉시 업데이트 → API 호출 → 실패 시 롤백 + +### 6.3 상태 관리 + +``` +초기 로딩: GET /v1/org-chart → 로컬 상태 세팅 + ├── departments (트리 구조) + ├── unassigned (미배치 직원 배열) + ├── hiddenDepartments (숨겨진 부서 배열) + ├── stats (통계) + └── company (회사 정보) + +변경 시: API 호출 → 성공 시 로컬 상태 업데이트 +``` + +--- + +## 7. MNG 참고 파일 + +| 파일 | 설명 | +|------|------| +| `mng/app/Http/Controllers/RdController.php` (L38-208) | 조직도 컨트롤러 로직 | +| `mng/resources/views/rd/org-chart.blade.php` | 조직도 UI (Alpine.js + SortableJS) | +| `mng/app/Models/Tenants/Department.php` | Department 모델 (options cast) | + +--- + +## 관련 문서 + +| 문서 | 경로 | +|------|------| +| 이관 계획서 | `dev/dev_plans/org-chart-service-migration-plan.md` | +| 조직도 시스템 (MNG) | `projects/org-chart/README.md` | +| React 아키텍처 | `frontend/v1/01-architecture.md` | +| API 패턴 | `frontend/v1/02-api-pattern.md` | +| 인증 흐름 | `frontend/v1/07-auth-flow.md` | + +--- + +**최종 업데이트**: 2026-03-22