Audit 모듈
📝 초안 (Draft)
이 문서는 검토 중입니다. 내용이 변경될 수 있습니다.
모델 변경 이력을 자동으로 기록하는 감사 로그 Core 모듈입니다.
개요
Audit 모듈은 모델의 CRUD 작업을 자동으로 추적하여 감사 로그를 생성합니다. Auditable Trait을 적용한 모델은 생성, 수정, 삭제 시 자동으로 변경 이력이 기록됩니다.
┌─────────────────────────────────────────────────────────────┐
│ Audit 모듈 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Model (Auditable Trait) │ │
│ │ │ │
│ │ create() ──┐ │ │
│ │ update() ──┼──► AuditObserver ──► AuditLog │ │
│ │ delete() ──┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 기록 정보: │
│ • 이벤트 (created, updated, deleted) │
│ • 변경 전/후 값 │
│ • 실행 사용자, 테넌트, IP, URL │
└─────────────────────────────────────────────────────────────┘
핵심 컴포넌트
| 컴포넌트 | 역할 |
|---|---|
Auditable | 모델에 적용하는 Trait |
AuditableInterface | Auditable 모델 계약 |
AuditObserver | 모델 이벤트 감지 및 로그 생성 |
AuditLog | 감사 로그 저장 모델 |
Auditable Trait 적용
기본 사용법
<?php
namespace App\Models;
use App\Core\Base\Audit\Contracts\AuditableInterface;
use App\Core\Base\Audit\Traits\Auditable;
use Illuminate\Database\Eloquent\Model;
class Product extends Model implements AuditableInterface
{
use Auditable;
protected $fillable = [
'name',
'price',
'description',
];
}
적용 즉시 동작
// 생성 → created 이벤트 기록
$product = Product::create([
'name' => 'New Product',
'price' => 10000,
]);
// 수정 → updated 이벤트 기록 (변경된 필드만)
$product->update(['price' => 15000]);
// 삭제 → deleted 이벤트 기록
$product->delete();
기록되는 정보
| 필드 | 설명 | 예시 |
|---|---|---|
auditable_type | 모델 클래스명 | App\Models\Product |
auditable_id | 모델 ID | 123 |
user_id | 실행한 사용자 ID | 1 |
tenant_id | 테넌트 ID | 5 |
event | 이벤트 유형 | created, updated, deleted |
old_values | 변경 전 값 (JSON) | {"price": 10000} |
new_values | 변경 후 값 (JSON) | {"price": 15000} |
ip_address | 클라이언트 IP | 192.168.1.1 |
user_agent | 브라우저 정보 | Mozilla/5.0... |
url | 요청 URL | /admin/products/123 |
metadata | 추가 메타데이터 | {"order_number": "ORD-001"} |
created_at | 기록 시간 | 2024-01-15 10:30:00 |
커스터마이징
특정 필드만 기록
class Order extends Model implements AuditableInterface
{
use Auditable;
/**
* 이 필드들만 감사 로그에 기록
*/
protected array $auditInclude = [
'status',
'total_amount',
'payment_status',
];
}
민감 정보 제외
class User extends Model implements AuditableInterface
{
use Auditable;
/**
* 이 필드들은 감사 로그에서 제외
*/
protected array $auditExclude = [
'password',
'remember_token',
'two_factor_secret',
'api_key',
];
}
특정 이벤트만 기록
class Document extends Model implements AuditableInterface
{
use Auditable;
/**
* 이 이벤트들만 기록 (삭제는 기록 안 함)
*/
protected array $auditEvents = [
'created',
'updated',
];
}
추가 메타데이터 포함
class Order extends Model implements AuditableInterface
{
use Auditable;
/**
* 감사 로그에 추가할 메타데이터
*/
public function getAuditMetadata(): array
{
return [
'order_number' => $this->order_number,
'customer_id' => $this->customer_id,
'customer_name' => $this->customer?->name,
];
}
}
조건부 활성화/비활성화
class Message extends Model implements AuditableInterface
{
use Auditable;
/**
* 감사 로그 활성화 여부
*/
public function isAuditEnabled(): bool
{
// Draft 상태에서는 기록 안 함
if ($this->status === 'draft') {
return false;
}
return config('core.audit.enabled', true);
}
}
일시 비활성화
특정 인스턴스만 비활성화
// 이 저장에서만 감사 로그 비활성화
$user->withoutAudit()->save();
// 체인 가능
$user->withoutAudit()->update(['name' => 'New Name']);
전역 비활성화 (마이그레이션, 시더)
// 콜백 내에서 모든 Auditable 모델의 로깅 비활성화
User::withoutAuditing(function () {
// 대량 데이터 삽입
User::factory()->count(1000)->create();
});
// 마이그레이션에서 사용
public function run(): void
{
Product::withoutAuditing(function () {
Product::insert([
['name' => 'Product 1', 'price' => 1000],
['name' => 'Product 2', 'price' => 2000],
// ...
]);
});
}
감사 로그 조회
AuditLog 모델 스코프
use App\Core\Base\Audit\Models\AuditLog;
// 특정 모델의 로그
$logs = AuditLog::forModel(Product::class)->get();
// 특정 이벤트의 로그
$logs = AuditLog::event('deleted')->get();
// 특정 사용자의 로그
$logs = AuditLog::byUser($userId)->get();
// 특정 테넌트의 로그
$logs = AuditLog::byTenant($tenantId)->get();
// 날짜 범위 조회
$logs = AuditLog::between('2024-01-01', '2024-01-31')->get();
// 조합 사용
$logs = AuditLog::forModel(User::class)
->event('updated')
->byTenant($tenantId)
->between($startDate, $endDate)
->latest()
->paginate(20);
모델에서 로그 조회
$product = Product::find(1);
// 해당 모델의 모든 감사 로그 (최신순)
$auditLogs = $product->auditLogs;
// 페이지네이션
$auditLogs = $product->auditLogs()->paginate(10);
// 특정 이벤트만
$deletedLogs = $product->auditLogs()
->where('event', 'deleted')
->get();
변경 내역 확인
$auditLog = AuditLog::find(1);
// 모든 변경 사항 (old와 new가 다른 필드만)
$changes = $auditLog->changes;
// [
// 'price' => ['old' => 10000, 'new' => 15000],
// 'name' => ['old' => 'Old Name', 'new' => 'New Name'],
// ]
// 개별 값
$oldValues = $auditLog->old_values;
$newValues = $auditLog->new_values;
관계 조회
$auditLog = AuditLog::find(1);
// 변경을 수행한 사용자
$user = $auditLog->user;
// 테넌트
$tenant = $auditLog->tenant;
// 감사 대상 모델
$product = $auditLog->auditable;
설정 (config/core.php)
'audit' => [
// 감사 로그 활성화
'enabled' => env('CORE_AUDIT_ENABLED', true),
// 저장 테이블명
'table' => 'audit_logs',
// 기록할 이벤트 (기본값)
'events' => ['created', 'updated', 'deleted'],
// 전역 제외 속성 (모든 모델에 적 용)
'exclude_attributes' => [
'password',
'remember_token',
'two_factor_secret',
],
],