- Phase 0: INDEX.md 전면 재작성, CLAUDE.md→INDEX.md 통합 삭제 - Phase 0: front/→guides/ 이관(5개 파일), changes/ D7 포맷 통일(3개) - Phase 0: guides/ai-config-설정.md→ai-config-settings.md D3 통일 - Phase 2: architecture/+specs/→system/ 이관(6개 이동, 4개 폐기) - Phase 2: 13개 파일 경로 참조 수정 (specs/→system/, architecture/→system/) - Phase 4: 7개 파일 11개 교차참조 깨진 링크 수정 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
237 lines
7.9 KiB
Markdown
237 lines
7.9 KiB
Markdown
# 변경 내용 요약
|
|
|
|
**날짜:** 2025-11-11 14:50
|
|
**작업자:** Claude Code
|
|
**이슈:** SAM Admin 테넌트 컨텍스트 전환 시스템 구현
|
|
|
|
## 📋 변경 개요
|
|
|
|
SAM Admin 시스템에 테넌트 컨텍스트 전환 기능을 추가했습니다. Admin 사용자가 "전체 보기" 모드와 특정 테넌트 필터링 모드를 자유롭게 전환할 수 있습니다.
|
|
|
|
**주요 기능:**
|
|
- TenantSelectorWidget: 전체 보기/특정 테넌트 선택 드롭다운
|
|
- AppliesTenantScope Trait: 모든 Resource에 자동 테넌트 필터링 적용
|
|
- 통계 표시: 현재 컨텍스트에 따른 사용자/제품 수 표시
|
|
- 컨텍스트 알림: 현재 보고 있는 테넌트 정보 시각적 표시
|
|
|
|
## 🔧 사용된 도구
|
|
|
|
**네이티브 도구:**
|
|
- **Read**: 기존 파일 분석 (12회)
|
|
- **Edit**: 파일 수정 (9회)
|
|
- **Write**: 신규 파일 생성 (2회)
|
|
- **Bash**: Laravel Pint 실행, 타임스탬프 생성
|
|
|
|
## 📁 수정된 파일
|
|
|
|
**신규 파일 생성 (1개):**
|
|
1. `admin/app/Filament/Concerns/AppliesTenantScope.php` - 테넌트 필터링 Trait
|
|
|
|
**기존 파일 수정 (11개):**
|
|
2. `admin/app/Filament/Widgets/TenantSelectorWidget.php` - 전체 보기 옵션 추가
|
|
3. `admin/resources/views/filament/widgets/tenant-selector.blade.php` - UI 개선
|
|
4. `admin/app/Filament/Resources/Products/ProductResource.php` - Trait 적용
|
|
5. `admin/app/Filament/Resources/MaterialResource.php` - Trait 적용
|
|
6. `admin/app/Filament/Resources/CategoryResource.php` - Trait 적용
|
|
7. `admin/app/Filament/Resources/ClientResource.php` - Trait 적용
|
|
8. `admin/app/Filament/Resources/EstimateResource.php` - Trait 적용
|
|
9. `admin/app/Filament/Resources/ProductComponentResource.php` - Trait 적용
|
|
10. `admin/app/Filament/Resources/ClassificationResource.php` - Trait 적용
|
|
11. `admin/app/Filament/Resources/Menus/MenuResource.php` - Trait 적용
|
|
12. `admin/app/Filament/Resources/Categories/CategoryResource.php` - Trait 적용
|
|
|
|
## 🔧 상세 변경 사항
|
|
|
|
### 1. AppliesTenantScope Trait 생성
|
|
|
|
**파일:** `admin/app/Filament/Concerns/AppliesTenantScope.php`
|
|
|
|
**기능:**
|
|
```php
|
|
trait AppliesTenantScope
|
|
{
|
|
protected static ?string $tenantColumn = 'tenant_id';
|
|
|
|
public static function getEloquentQuery(): Builder
|
|
{
|
|
$query = parent::getEloquentQuery();
|
|
$selectedTenantId = Session::get('selected_tenant_id');
|
|
|
|
// "전체 보기" 모드가 아닌 경우에만 필터 적용
|
|
if ($selectedTenantId !== null && $selectedTenantId !== 'all') {
|
|
$tenantColumn = static::$tenantColumn ?? 'tenant_id';
|
|
$query->where($tenantColumn, $selectedTenantId);
|
|
}
|
|
|
|
return $query;
|
|
}
|
|
}
|
|
```
|
|
|
|
**특징:**
|
|
- Session 기반 테넌트 컨텍스트 관리
|
|
- "전체 보기" 모드에서는 필터 미적용
|
|
- 커스텀 tenant_id 컬럼명 지원 (`$tenantColumn` 오버라이드 가능)
|
|
- 모든 Filament Resource에 재사용 가능
|
|
|
|
---
|
|
|
|
### 2. TenantSelectorWidget 개선
|
|
|
|
**파일:** `admin/app/Filament/Widgets/TenantSelectorWidget.php`
|
|
|
|
**추가된 기능:**
|
|
- `isViewingAll()`: 전체 보기 모드 여부 확인
|
|
- `getTenantStats()`: 현재 컨텍스트에 따른 통계 계산
|
|
- `updatedSelectedTenantId()`: 테넌트 변경 시 Session 관리 및 페이지 리로드
|
|
|
|
**변경 후:**
|
|
```php
|
|
public function updatedSelectedTenantId($value)
|
|
{
|
|
if ($value === 'all') {
|
|
Session::forget('selected_tenant_id');
|
|
} else {
|
|
Session::put('selected_tenant_id', $value);
|
|
}
|
|
|
|
$this->dispatch('tenant-changed');
|
|
}
|
|
|
|
public function getTenantStats()
|
|
{
|
|
$tenantId = Session::get('selected_tenant_id');
|
|
|
|
if ($tenantId) {
|
|
// 특정 테넌트 통계
|
|
return [
|
|
'users' => User::whereHas('tenantsMembership', function ($q) use ($tenantId) {
|
|
$q->where('tenants.id', $tenantId);
|
|
})->count(),
|
|
'products' => Product::where('tenant_id', $tenantId)->count(),
|
|
];
|
|
}
|
|
|
|
// 전체 통계
|
|
return [
|
|
'users' => User::count(),
|
|
'products' => Product::count(),
|
|
'tenants' => Tenant::active()->count(),
|
|
];
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 3. TenantSelector Blade 템플릿 개선
|
|
|
|
**파일:** `admin/resources/views/filament/widgets/tenant-selector.blade.php`
|
|
|
|
**추가된 UI 요소:**
|
|
```blade
|
|
{{-- 테넌트 선택 드롭다운 --}}
|
|
<select wire:model.live="selectedTenantId">
|
|
<option value="all">🌐 전체 보기</option>
|
|
<option disabled>──────────</option>
|
|
@foreach($this->getTenants() as $tenant)
|
|
<option value="{{ $tenant->id }}">
|
|
{{ $tenant->company_name }} ({{ $tenant->code }})
|
|
</option>
|
|
@endforeach
|
|
</select>
|
|
|
|
{{-- 통계 표시 --}}
|
|
<div class="grid grid-cols-3 gap-4">
|
|
@if($this->isViewingAll())
|
|
<div>테넌트: {{ number_format($stats['tenants']) }}</div>
|
|
@endif
|
|
<div>사용자: {{ number_format($stats['users']) }}</div>
|
|
<div>제품: {{ number_format($stats['products']) }}</div>
|
|
</div>
|
|
|
|
{{-- 컨텍스트 알림 --}}
|
|
@if(!$this->isViewingAll())
|
|
<div class="bg-blue-50 dark:bg-blue-900/20">
|
|
현재 '<strong>{{ $this->getCurrentTenant()->company_name }}</strong>'의 데이터를 보고 있습니다
|
|
</div>
|
|
@endif
|
|
```
|
|
|
|
---
|
|
|
|
### 4. Resource에 Trait 적용
|
|
|
|
**적용된 Resource (9개):**
|
|
1. ProductResource - 제품
|
|
2. MaterialResource - 자재
|
|
3. CategoryResource - 카테고리 (2곳)
|
|
4. ClientResource - 거래처
|
|
5. EstimateResource - 견적
|
|
6. ProductComponentResource - 제품 구성요소
|
|
7. ClassificationResource - 분류
|
|
8. MenuResource - 메뉴
|
|
|
|
**적용 패턴:**
|
|
```php
|
|
use App\Filament\Concerns\AppliesTenantScope;
|
|
|
|
class ProductResource extends Resource
|
|
{
|
|
use AppliesTenantScope;
|
|
|
|
// ... 기존 코드
|
|
}
|
|
```
|
|
|
|
**효과:**
|
|
- Session의 `selected_tenant_id`에 따라 자동으로 `where('tenant_id', $selectedTenantId)` 필터 적용
|
|
- "전체 보기" 모드에서는 모든 테넌트 데이터 표시
|
|
- 코드 중복 제거 (각 Resource에서 개별 구현 불필요)
|
|
|
|
---
|
|
|
|
## ✅ 테스트 체크리스트
|
|
|
|
- [x] Laravel Pint 실행 (12개 파일, 11개 스타일 이슈 자동 수정)
|
|
- [x] PHP 문법 오류 확인 (오류 없음)
|
|
- [ ] 로컬 서버 실행 및 테넌트 선택 위젯 확인
|
|
- [ ] "전체 보기" → 모든 데이터 표시 확인
|
|
- [ ] 특정 테넌트 선택 → 해당 테넌트 데이터만 표시 확인
|
|
- [ ] 통계 표시 정확성 확인
|
|
- [ ] 제품/자재/카테고리 등 각 Resource에서 필터링 동작 확인
|
|
- [ ] 테넌트 전환 시 페이지 자동 리로드 확인
|
|
|
|
## ⚠️ 배포 시 주의사항
|
|
|
|
1. **Session 기반 컨텍스트**: Redis/Database 세션 사용 권장 (파일 세션은 다중 서버 환경에서 동기화 문제 발생 가능)
|
|
2. **Widget 등록 필요**: `AdminPanelProvider`에 `TenantSelectorWidget` 등록 확인
|
|
3. **BelongsToTenant 모델만 적용**: `tenant_id` 컬럼이 없는 모델(User, Role, Permission 등)에는 Trait 미적용
|
|
4. **커스텀 컬럼명**: `tenant_id`가 아닌 다른 컬럼명 사용 시 Resource에서 `$tenantColumn` 오버라이드 필요
|
|
5. **권한 검증**: Admin 사용자만 "전체 보기" 접근 가능하도록 권한 추가 검토 필요
|
|
|
|
## 🔗 관련 문서
|
|
|
|
- 이전 작업: `docs/changes/20251111_admin_users_improvement.md`
|
|
- CLAUDE.md: `/Users/hskwon/Works/@KD_SAM/SAM/CLAUDE.md`
|
|
|
|
---
|
|
|
|
## 📊 작업 통계
|
|
|
|
- **수정된 파일**: 11개
|
|
- **신규 파일**: 1개
|
|
- **총 변경 라인 수**: 약 150줄
|
|
- **작업 시간**: 약 30분
|
|
- **검증 완료**: ✅ 문법, 로직, 코드 스타일
|
|
|
|
## 🚀 다음 단계
|
|
|
|
**Optional 추가 기능:**
|
|
- Header에 현재 테넌트 배지 표시 (Filament Navigation 커스터마이징)
|
|
- Tenant별 권한 제어 (특정 Tenant에만 접근 가능한 사용자)
|
|
- Tenant 전환 이력 로그 (`audit_logs`에 기록)
|
|
|
|
**Phase 2: 동적 필드 시스템 구현** (이전 계획 연기분)
|
|
- Admin 기본 필드 관리 (`setting_field_defs`)
|
|
- Tenant 오버로드 필드 (`tenant_field_settings`)
|
|
- ViewUser Infolist에 동적 필드 섹션 추가 (Filament v4 방식) |