Guardian 확장
📝 초안 (Draft)
이 문서는 검토 중입니다. 내용이 변경될 수 있습니다.
보호자-피보호자 관계를 관리하는 Core Extension입니다.
개요
Guardian 확장은 교육(LMS), 의료, 금융 분야에서 미성년자/환자/피보호자 관리에 활용됩니다. 보호자(Guardian)와 피보호자(Ward) 간의 관계를 생성, 승인, 관리하며, 권한 카테고리별로 접근을 제어합니다.
┌─────────────────────────────────────────────────────────────┐
│ Guardian Extension │
│ │
│ ┌─────────────┐ 요청 ┌─────────────┐ │
│ │ Guardian │ ─────────► │ Ward │ │
│ │ (보호자) │ │ (피보호자) │ │
│ └─────────────┘ └─────────────┘ │
│ │ │ │
│ │ 승인/거절 │ │
│ ◄────────────────────────── │
│ │
│ 상태: pending → approved/rejected → expired │
│ 권한: academic, financial, medical, activity, communication │
└─────────────────────────────────────────────────────────────┘
활성화
Guardian은 선택적 확장 모듈로, 필요한 프로젝트에서만 활성화합니다.
# .env
CORE_GUARDIAN_ENABLED=true
// config/core.php
'guardian' => [
'enabled' => env('CORE_GUARDIAN_ENABLED', false),
'auto_expire_days' => 365, // 자동 만료 기간 (null = 무기한)
],
구조
packages/core/Extensions/Guardian/
├── Contracts/
│ ├── GuardianInterface.php # Guardian 역할 인터페이스
│ ├── WardInterface.php # Ward(피보호자) 역할 인터페이스
│ └── GuardianServiceInterface.php # 서비스 인터페이스
├── Enums/
│ ├── RelationshipType.php # parent, teacher, mentor, observer
│ ├── RelationshipStatus.php # pending, approved, rejected, expired
│ └── PermissionCategory.php # academic, financial, medical, ...
├── Models/
│ └── GuardianRelationship.php # Eloquent 모델
├── Services/
│ └── GuardianService.php # 비즈니스 로직
├── Traits/
│ ├── HasGuardians.php # Ward에 적용 (피보호자)
│ └── IsGuardian.php # Guardian에 적용 (보호자)
├── Events/
│ ├── RelationshipRequested.php
│ ├── RelationshipApproved.php
│ ├── RelationshipRejected.php
│ └── RelationshipExpired.php
└── Providers/
└── GuardianServiceProvider.php
관계 유형 (RelationshipType)
| 유형 | 설명 | 기본 권한 | 우선순위 |
|---|---|---|---|
parent | 보호자/학부모 | 전체 (5개 모두) | 1 (최고) |
teacher | 교사/강사 | academic, activity | 2 |
mentor | 멘토 | academic, activity, communication | 3 |
observer | 관찰자 | academic (읽기 전용) | 4 (최저) |
use App\Core\Extensions\Guardian\Enums\RelationshipType;
// 유형별 정보
RelationshipType::PARENT->label(); // "Parent/Guardian"
RelationshipType::PARENT->description(); // "Legal guardian with full access..."
RelationshipType::PARENT->defaultPermissions(); // [ACADEMIC, FINANCIAL, MEDICAL, ...]
RelationshipType::PARENT->priority(); // 1
RelationshipType::PARENT->requiresApproval(); // true
RelationshipType::PARENT->canManageGuardians(); // true (parent만)
권한 카테고리 (PermissionCategory)
| 카테고리 | 설명 | 예시 |
|---|---|---|
academic | 학업 정보 | 성적, 과제, 출석, 시험 |
financial | 재정 정보 | 결제, 청구서, 환불 |
medical | 건강 정보 | 건강 기록, 알레르기 |
activity | 활동 기록 | 로그인, 진도, 수강 이력 |
communication | 소통 | 메시지, 알림, 공지 |
권한 제한
financial과 medical은 parent 유형만 가질 수 있습니다. teacher, mentor, observer는 이 권한을 요청해도 거부됩니다.
관계 상태 (RelationshipStatus)
┌─────────┐ approve() ┌──────────┐ expire() ┌─────────┐
│ PENDING │ ─────────────────► │ APPROVED │ ─────────────────► │ EXPIRED │
└─────────┘ └──────────┘ └─────────┘
│
│ reject()
▼
┌──────────┐
│ REJECTED │
└──────────┘
| 상태 | 설명 | 다음 상태 |
|---|---|---|
pending | 승인 대기 중 | approved, rejected |
approved | 승인됨 (활성) | expired |
rejected | 거부됨 (종료) | - |
expired | 만료됨 (종료) | - |
User 모델 설정
User 모델에 Trait을 추가하여 Guardian/Ward 기능을 활성화합니다.
use App\Core\Extensions\Guardian\Traits\HasGuardians;
use App\Core\Extensions\Guardian\Traits\IsGuardian;
class User extends Authenticatable
{
use HasGuardians; // 피보호자가 될 수 있음
use IsGuardian; // 보호자가 될 수 있음
}
GuardianService 사용법
관계 생성
use App\Core\Extensions\Guardian\Services\GuardianService;
use App\Core\Extensions\Guardian\Enums\RelationshipType;
use App\Core\Extensions\Guardian\Enums\PermissionCategory;
$service = app(GuardianService::class);
// 기본 권한으로 관계 생성
$relationship = $service->createRelationship(
guardian: $parentUser,
ward: $childUser,
type: RelationshipType::PARENT
);
// 특정 권한만 지정
$relationship = $service->createRelationship(
guardian: $teacherUser,
ward: $studentUser,
type: RelationshipType::TEACHER,
permissions: [
PermissionCategory::ACADEMIC,
PermissionCategory::ACTIVITY,
]
);
// 유효 기간 지정
$relationship = $service->createRelationship(
guardian: $mentorUser,
ward: $menteeUser,
type: RelationshipType::MENTOR,
validFrom: now(),
validUntil: now()->addYear()
);
관계 승인/거절
// 승인
$service->approveRelationship($relationship, $approverUser);
// 거절 (사유 포함)
$service->rejectRelationship(
$relationship,
$rejectorUser,
'서류 미비로 인한 거절'
);
// 만료 처리
$service->expireRelationship($relationship);
// 즉시 취소 (revoke)
$service->revokeRelationship(
$relationship,
$adminUser,
'관리자 요청으로 취소'
);
권한 검사
// 특정 권한 카테고리 접근 가능 여부
$canAccess = $service->canAccess(
guardian: $parentUser,
ward: $childUser,
category: PermissionCategory::ACADEMIC
);
// 활성 관계가 있는지만 확인 (카테고리 무관)
$hasRelation = $service->canAccess(
guardian: $parentUser,
ward: $childUser,
category: null
);
// 특정 관계 조회
$relationship = $service->getRelationship(
guardian: $parentUser,
ward: $childUser,
type: RelationshipType::PARENT
);
관계 조회
// 사용자의 모든 활성 관계 (guardian이든 ward이든)
$relationships = $service->getActiveRelationships($user);
// 대기 중인 요청 조회
$pendingRequests = $service->getPendingRequests($user);
권한 관리
// 권한 업데이트
$service->updatePermissions($relationship, [
PermissionCategory::ACADEMIC,
PermissionCategory::ACTIVITY,
PermissionCategory::COMMUNICATION,
]);
// 유효 기간 연장
$service->extendValidity($relationship, now()->addMonths(6));
만료 처리 (배치)
// 만료된 관계 일괄 처리 (스케줄러에서 사용)
$expiredCount = $service->processExpiredRelationships();
Trait 메서드
IsGuardian (보호자 Trait)
// 피보호자 목록 조회
$wards = $guardianUser->getWards();
// 유형별 피보호자 조회
$students = $teacherUser->getWardsByType(RelationshipType::TEACHER);
// 특정 피보호자 접근 가능 여부
$canAccess = $guardianUser->canAccessWard($wardUser, PermissionCategory::ACADEMIC);
// 보호자 관계인지 확인
$isGuardian = $parentUser->isGuardianOf($childUser);
// 관계 요청 생성
$relationship = $guardianUser->requestGuardianship(
$wardUser,
RelationshipType::MENTOR,
[PermissionCategory::ACADEMIC]
);
HasGuardians (피보호자 Trait)
// 보호자 목록 조회
$guardians = $wardUser->getGuardians();
// 유형별 보호자 조회
$parents = $studentUser->getGuardiansByType(RelationshipType::PARENT);
// 주 보호자 조회 (parent 우선, 없으면 가장 높은 우선순위)
$primaryGuardian = $wardUser->getPrimaryGuardian();
// 특정 보호자가 있는지 확인
$hasGuardian = $wardUser->hasGuardian($parentUser);
// 대기 중인 요청 조회
$pendingRequests = $wardUser->getPendingGuardianRequests();
// 관계 승인/거절
$wardUser->approveGuardian($relationship);
$wardUser->rejectGuardian($relationship, '거절 사유');
GuardianRelationship 모델
상태 확인 메서드
$relationship->isPending(); // 대기 중?
$relationship->isApproved(); // 승인됨?
$relationship->isRejected(); // 거부됨?
$relationship->isExpired(); // 만료됨?
$relationship->isActive(); // 현재 활성? (approved + 유효기간 내)
$relationship->hasExpiredValidity(); // 유효기간 지남?
권한 관리 메서드
// 권한 확인
$hasPermission = $relationship->hasPermission(PermissionCategory::ACADEMIC);
// 권한 목록 (Enum 배열)
$categories = $relationship->getPermissionCategories();
// 권한 추가/제거
$relationship->addPermission(PermissionCategory::COMMUNICATION);
$relationship->removePermission(PermissionCategory::FINANCIAL);
$relationship->save();
Query Scopes
use App\Core\Extensions\Guardian\Models\GuardianRelationship;
// 활성 관계만
GuardianRelationship::active()->get();
// 대기 중인 관계만
GuardianRelationship::pending()->get();
// 특정 Guardian의 관계
GuardianRelationship::forGuardian($userId)->get();
// 특정 Ward의 관계
GuardianRelationship::forWard($userId)->get();
// 특정 유형의 관계
GuardianRelationship::ofType(RelationshipType::PARENT)->get();
// 유효 기간 내 관계
GuardianRelationship::withinValidity()->get();
// 현재 활성 관계 (approved + 유효기간 내)
GuardianRelationship::currentlyActive()->get();
// 만료된 관계
GuardianRelationship::expired()->get();
Filament 연동
Tenant Panel에서 GuardianRelationshipResource로 관계를 관리할 수 있습니다.
// app/Filament/Tenant/Resources/GuardianRelationshipResource.php
class GuardianRelationshipResource extends Resource
{
protected static ?string $model = GuardianRelationship::class;
protected static ?string $navigationIcon = 'heroicon-o-users';
protected static ?string $navigationGroup = 'Guardian';
// 목록, 생성, 수정, 삭제
// Approve/Reject 액션 (확인 모달)
}
기능:
- 관계 목록 조회 (상태별 필터)
- 관계 생성/수정/삭제
- Approve/Reject 액션 (확인 모달)
이벤트
관계 상태 변경 시 이벤트가 발생합니다.
| 이벤트 | 발생 시점 |
|---|---|
RelationshipRequested | 관계 요청 생성 시 |
RelationshipApproved | 관계 승인 시 |
RelationshipRejected | 관계 거부 시 |
RelationshipExpired | 관계 만료 시 |
// 이벤트 리스너 등록
Event::listen(RelationshipApproved::class, function ($event) {
// 승인 알림 발송
$event->relationship->guardian->notify(
new GuardianshipApprovedNotification($event->relationship)
);
});
테스트
# Unit 테스트
php artisan test tests/Unit/Core/Guardian/
# Feature 테스트 (Filament 액션)
php artisan test tests/Feature/Filament/Guardian/
테스트 커버리지:
- Unit: 72 tests (Enums, Model, Service, Traits)
- Feature: 16 tests (Approve/Reject actions)
테스트 예제
// tests/Unit/Core/Guardian/GuardianServiceTest.php
it('creates a guardian relationship', function () {
$guardian = User::factory()->create();
$ward = User::factory()->create();
$service = app(GuardianService::class);
$relationship = $service->createRelationship(
guardian: $guardian,
ward: $ward,
type: RelationshipType::PARENT
);
expect($relationship)
->guardian_id->toBe($guardian->id)
->ward_id->toBe($ward->id)
->status->toBe(RelationshipStatus::PENDING->value);
});
it('checks access permission correctly', function () {
$guardian = User::factory()->create();
$ward = User::factory()->create();
$service = app(GuardianService::class);
$relationship = $service->createRelationship(
guardian: $guardian,
ward: $ward,
type: RelationshipType::TEACHER,
permissions: [PermissionCategory::ACADEMIC]
);
// 승인 전
expect($service->canAccess($guardian, $ward, PermissionCategory::ACADEMIC))
->toBeFalse();
// 승인 후
$service->approveRelationship($relationship, $ward);
expect($service->canAccess($guardian, $ward, PermissionCategory::ACADEMIC))
->toBeTrue();
expect($service->canAccess($guardian, $ward, PermissionCategory::FINANCIAL))
->toBeFalse(); // TEACHER는 FINANCIAL 권한 없음
});
활용 사례
LMS (학습 관리 시스템)
// 학부모-학생 관계
$service->createRelationship(
guardian: $parent,
ward: $student,
type: RelationshipType::PARENT
);
// 교사-학생 관계 (학급 단위)
foreach ($classStudents as $student) {
$service->createRelationship(
guardian: $teacher,
ward: $student,
type: RelationshipType::TEACHER,
validUntil: $semester->end_date
);
}
의료 시스템
// 보호자-환자 관계
$service->createRelationship(
guardian: $familyMember,
ward: $patient,
type: RelationshipType::PARENT,
permissions: [
PermissionCategory::MEDICAL,
PermissionCategory::FINANCIAL,
]
);
멘토링 프로그램
// 멘토-멘티 관계
$service->createRelationship(
guardian: $mentor,
ward: $mentee,
type: RelationshipType::MENTOR,
validFrom: $program->start_date,
validUntil: $program->end_date
);