# Department Tree API (부서트리 조회 API) 규칙 ## 개요 부서트리 API는 테넌트 내 조직도를 계층 구조로 조회하는 API입니다. `departments` 테이블의 `parent_id`를 통한 자기참조 관계로 무한 depth 계층 구조를 지원합니다. ## 핵심 모델 ### Department - **위치**: `App\Models\Tenants\Department` - **역할**: 부서/조직 정보 - **특징**: - `parent_id` 자기참조로 계층 구조 - `HasRoles` 트레이트 (부서도 권한/역할 보유 가능) - `ModelTrait` 적용 (is_active, 날짜 처리) ## 엔드포인트 ### 부서 트리 전용 | Method | Path | 설명 | |--------|------|------| | GET | `/v1/departments/tree` | 부서 트리 조회 | ### 기본 CRUD (참고) | Method | Path | 설명 | |--------|------|------| | GET | `/v1/departments` | 부서 목록 조회 | | GET | `/v1/departments/{id}` | 부서 상세 조회 | | POST | `/v1/departments` | 부서 생성 | | PATCH | `/v1/departments/{id}` | 부서 수정 | | DELETE | `/v1/departments/{id}` | 부서 삭제 | ### 부서-사용자 관리 | Method | Path | 설명 | |--------|------|------| | GET | `/v1/departments/{id}/users` | 부서 사용자 목록 | | POST | `/v1/departments/{id}/users` | 사용자 배정 | | DELETE | `/v1/departments/{id}/users/{user}` | 사용자 제거 | | PATCH | `/v1/departments/{id}/users/{user}/primary` | 주부서 설정 | ## 데이터 구조 ### 기본 필드 | 필드 | 타입 | 설명 | |------|------|------| | `id` | int | PK | | `tenant_id` | int | 테넌트 ID | | `parent_id` | int | 상위 부서 ID (nullable, 최상위는 null) | | `code` | string | 부서 코드 (unique) | | `name` | string | 부서명 | | `description` | string | 부서 설명 | | `is_active` | bool | 활성화 상태 | | `sort_order` | int | 정렬 순서 | | `created_by` | int | 생성자 | | `updated_by` | int | 수정자 | | `deleted_by` | int | 삭제자 | ### 트리 응답 구조 ```json [ { "id": 1, "tenant_id": 1, "parent_id": null, "code": "DEPT001", "name": "경영지원본부", "is_active": true, "sort_order": 1, "children": [ { "id": 2, "tenant_id": 1, "parent_id": 1, "code": "DEPT002", "name": "인사팀", "is_active": true, "sort_order": 1, "children": [], "users": [] }, { "id": 3, "tenant_id": 1, "parent_id": 1, "code": "DEPT003", "name": "재무팀", "is_active": true, "sort_order": 2, "children": [], "users": [] } ], "users": [ { "id": 1, "name": "홍길동", "email": "hong@example.com" } ] } ] ``` ## 트리 조회 로직 ### tree() 메서드 구현 ```php public function tree(array $params = []): array { // 1. 파라미터 검증 $withUsers = filter_var($params['with_users'] ?? false, FILTER_VALIDATE_BOOLEAN); // 2. 최상위 부서 조회 (parent_id가 null) $query = Department::query() ->whereNull('parent_id') ->orderBy('sort_order') ->orderBy('name'); // 3. 재귀적으로 자식 부서 로드 $query->with(['children' => function ($q) use ($withUsers) { $q->orderBy('sort_order')->orderBy('name'); $this->loadChildrenRecursive($q, $withUsers); }]); // 4. 사용자 포함 옵션 if ($withUsers) { $query->with(['users:id,name,email']); } return $query->get()->toArray(); } // 재귀 로딩 헬퍼 private function loadChildrenRecursive($query, bool $withUsers): void { $query->with(['children' => function ($q) use ($withUsers) { $q->orderBy('sort_order')->orderBy('name'); $this->loadChildrenRecursive($q, $withUsers); }]); if ($withUsers) { $query->with(['users:id,name,email']); } } ``` ### 정렬 규칙 1. `sort_order` 오름차순 2. `name` 오름차순 (동일 sort_order일 때) ## 요청 파라미터 ### GET /v1/departments/tree | 파라미터 | 타입 | 기본값 | 설명 | |----------|------|--------|------| | `with_users` | bool | false | 부서별 사용자 목록 포함 | ### 예시 ```bash # 기본 트리 조회 GET /v1/departments/tree # 사용자 포함 트리 조회 GET /v1/departments/tree?with_users=1 ``` ## 관계 (Relationships) ```php public function parent(): BelongsTo // 상위 부서 public function children() // 하위 부서들 (HasMany) public function users() // 소속 사용자들 (BelongsToMany) public function departmentUsers() // 부서-사용자 pivot (HasMany) public function permissionOverrides() // 권한 오버라이드 (MorphMany) ``` ## 부서-사용자 관계 (Pivot) ### department_user 테이블 | 필드 | 타입 | 설명 | |------|------|------| | `department_id` | int | 부서 ID | | `user_id` | int | 사용자 ID | | `tenant_id` | int | 테넌트 ID | | `is_primary` | bool | 주부서 여부 | | `joined_at` | timestamp | 배정일 | | `left_at` | timestamp | 해제일 | | `deleted_at` | timestamp | Soft Delete | ### 주부서 규칙 - 한 사용자는 여러 부서에 소속 가능 - 주부서(`is_primary`)는 사용자당 1개만 가능 - 주부서 설정 시 기존 주부서는 자동 해제 ## 권한 관리 ### 부서 권한 시스템 부서는 Spatie Permission과 연동되어 권한을 가질 수 있습니다. - **ALLOW**: `model_has_permissions` 테이블 - **DENY**: `permission_overrides` 테이블 (effect: -1) ### 관련 엔드포인트 | Method | Path | 설명 | |--------|------|------| | GET | `/v1/departments/{id}/permissions` | 부서 권한 목록 | | POST | `/v1/departments/{id}/permissions` | 권한 부여/차단 | | DELETE | `/v1/departments/{id}/permissions/{permission}` | 권한 제거 | ## 주의사항 1. **무한 재귀 방지**: Eloquent eager loading으로 처리, 별도 depth 제한 없음 2. **성능 고려**: 대규모 조직도의 경우 `with_users` 사용 시 응답 시간 증가 3. **정렬 일관성**: 모든 레벨에서 동일한 정렬 규칙 적용 4. **멀티테넌트**: tenant_id 기반 자동 스코핑 5. **주부서 제약**: 사용자당 주부서 1개만 허용 6. **Soft Delete**: department_user pivot도 Soft Delete 적용 ## 트리 구축 예시 ### 조직도 예시 ``` 경영지원본부 (parent_id: null) ├── 인사팀 (parent_id: 1) │ ├── 채용파트 (parent_id: 2) │ └── 교육파트 (parent_id: 2) ├── 재무팀 (parent_id: 1) └── 총무팀 (parent_id: 1) 개발본부 (parent_id: null) ├── 프론트엔드팀 (parent_id: 4) ├── 백엔드팀 (parent_id: 4) └── QA팀 (parent_id: 4) ``` ### SQL 예시 (데이터 삽입) ```sql -- 최상위 부서 INSERT INTO departments (tenant_id, parent_id, code, name, sort_order) VALUES (1, NULL, 'HQ', '경영지원본부', 1); -- 하위 부서 INSERT INTO departments (tenant_id, parent_id, code, name, sort_order) VALUES (1, 1, 'HR', '인사팀', 1); ```