docs: [org-chart] 조직도 서비스 이관 기획서 및 React FE 가이드

- 이관 계획서 (API 완료, React 문서 제공)
- React FE 구현 가이드 (API 스펙, UI 설계, 드래그앤드롭)
- INDEX.md 업데이트
This commit is contained in:
김보곤
2026-03-22 17:22:14 +09:00
parent 419e438a8a
commit 85bc6e2f08
3 changed files with 427 additions and 0 deletions

View File

@@ -46,6 +46,8 @@
| 경조사비 서비스 이관 | `dev/dev_plans/condolence-expense-service-plan.md` | MNG 경조사비 → API+React 서비스 이관 기획 | | 경조사비 서비스 이관 | `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/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/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` | 서버 운영 매뉴얼 | | 서버 운영 | `dev/deploys/ops-manual/README.md` | 서버 운영 매뉴얼 |
| 서버 접근/백업 | `system/server-access-management.md` | 계정, 권한, 백업, 리플리케이션 | | 서버 접근/백업 | `system/server-access-management.md` | 계정, 권한, 백업, 리플리케이션 |
| 이관 작업 | `system/migration-status.md` | MNG→API+React 이관 현황, 우선순위, 로드맵 | | 이관 작업 | `system/migration-status.md` | MNG→API+React 이관 현황, 우선순위, 로드맵 |

View File

@@ -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

View File

@@ -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