# Super Admin Protection Feature
**날짜:** 2025-12-01
**작업자:** Claude Code
**요청:** 슈퍼관리자 보호 및 복원/영구삭제 권한 분리
---
## 1. 요구사항
### 1.1 슈퍼관리자 보호
- 일반관리자는 슈퍼관리자를 **볼 수 없음** (목록에서 숨김)
- 일반관리자는 슈퍼관리자를 **수정/삭제할 수 없음**
- 슈퍼관리자만 다른 슈퍼관리자를 관리 가능
### 1.2 복원/영구삭제 권한 분리
- **복원 (Restore)**: 일반관리자도 가능
- **영구삭제 (Force Delete)**: 슈퍼관리자 전용
---
## 2. 구현 내용
### 2.1 라우트 수정 (`routes/api.php`)
8개 엔티티의 restore 라우트를 `super.admin` 미들웨어 밖으로 이동:
| 엔티티 | 라인 | 복원 라우트 | 영구삭제 라우트 |
|--------|------|-------------|-----------------|
| Tenants | 42-48 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) |
| Departments | 76-82 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) |
| Users | 93-99 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) |
| Menus | 117-123 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) |
| Boards | 151-157 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) |
| PM Projects | 234-240 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) |
| PM Tasks | 260-266 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) |
| PM Issues | 292-298 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) |
**패턴:**
```php
// 복원 (일반관리자 가능)
Route::post('/{id}/restore', [Controller::class, 'restore'])->name('restore');
// 슈퍼관리자 전용 액션 (영구삭제)
Route::middleware('super.admin')->group(function () {
Route::delete('/{id}/force', [Controller::class, 'forceDestroy'])->name('forceDestroy');
});
```
### 2.2 서비스 레이어 수정
#### `app/Services/UserService.php`
```php
public function canAccessUser(int $targetUserId): bool
{
// withTrashed()를 사용하여 soft-deleted 사용자도 확인 (복원 시 필요)
$targetUser = User::withTrashed()->find($targetUserId);
$currentUser = auth()->user();
// 대상 사용자가 슈퍼관리자이고 현재 사용자가 슈퍼관리자가 아니면 접근 불가
if ($targetUser?->is_super_admin && ! $currentUser?->is_super_admin) {
return false;
}
return true;
}
```
#### `app/Services/UserPermissionService.php`
```php
public function canModifyUser(int $targetUserId): bool
{
// withTrashed()를 사용하여 일관성 유지
$targetUser = User::withTrashed()->find($targetUserId);
$currentUser = auth()->user();
if ($targetUser?->is_super_admin && ! $currentUser?->is_super_admin) {
return false;
}
return true;
}
```
**핵심 수정**: `User::find()` → `User::withTrashed()->find()`
- Soft-deleted 사용자도 조회 가능하게 변경
- 복원 작업 시 권한 체크가 정상 작동
### 2.3 뷰 레이어 수정
6개 테이블 뷰에 권한별 버튼 표시 로직 적용:
| 파일 | 복원 버튼 | 영구삭제 버튼 |
|------|-----------|---------------|
| `users/partials/table.blade.php` | `$canModify` 체크 | `is_super_admin` 체크 |
| `users/partials/modal-info.blade.php` | 슈퍼관리자이거나 대상이 일반사용자 | - |
| `departments/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 |
| `menus/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 |
| `boards/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 |
| `tenants/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 |
| `project-management/projects/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 |
**Blade 패턴:**
```blade
@if($item->deleted_at)
@if(auth()->user()?->is_super_admin)
@endif
@endif
```
---
## 3. 수정된 파일 목록
### 라우트
- `routes/api.php` - 8개 엔티티 restore 라우트 분리
### 서비스
- `app/Services/UserService.php` - `canAccessUser()` withTrashed 적용
- `app/Services/UserPermissionService.php` - `canModifyUser()` withTrashed 적용
### 뷰 (Blade)
- `resources/views/users/partials/table.blade.php`
- `resources/views/users/partials/modal-info.blade.php`
- `resources/views/departments/partials/table.blade.php`
- `resources/views/menus/partials/table.blade.php`
- `resources/views/boards/partials/table.blade.php`
- `resources/views/tenants/partials/table.blade.php`
- `resources/views/project-management/projects/partials/table.blade.php`
---
## 4. 테스트 시나리오
### 4.1 일반관리자 테스트
- [ ] 사용자 목록에서 슈퍼관리자가 보이지 않음
- [ ] 삭제된 사용자 복원 가능
- [ ] 삭제된 부서/메뉴/게시판/테넌트/프로젝트 복원 가능
- [ ] 영구삭제 버튼이 보이지 않음
- [ ] 슈퍼관리자 수정/삭제 불가 (API 레벨)
### 4.2 슈퍼관리자 테스트
- [ ] 모든 사용자 조회 가능 (슈퍼관리자 포함)
- [ ] 삭제된 항목 복원 가능
- [ ] 영구삭제 가능
- [ ] 다른 슈퍼관리자 관리 가능
---
## 5. 이슈 해결
### 5.1 302 Found 에러
**문제**: 일반관리자가 복원 API 호출 시 302 리다이렉트 발생
**원인**: restore 라우트가 `super.admin` 미들웨어 내부에 있었음
**해결**: restore 라우트를 미들웨어 밖으로 이동
### 5.2 Soft-deleted 사용자 권한 체크 실패
**문제**: `User::find()`가 soft-deleted 사용자를 조회하지 못함
**원인**: Eloquent 기본 동작으로 soft-deleted 레코드 제외
**해결**: `User::withTrashed()->find()` 사용
---
## 6. 관련 문서
- `claudedocs/archive-restore-feature-analysis.md` - 아카이브/복원 기능 분석
- `CURRENT_WORKS.md` - 작업 히스토리