Skip to main content

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소통메시지, 알림, 공지
권한 제한

financialmedicalparent 유형만 가질 수 있습니다. 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
);

관련 문서