본문으로 건너뛰기

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, activity2
mentor멘토academic, activity, communication3
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
);

관련 문서​