개요

웹 서비스를 구현하다 보면 사용자가 실수로 데이타를 삭제하거나 수정하는 경우에 대비해야 할 필요가 있습니다.

DBMS 차원의 백업과 복구는 시스템 장애나 해킹등의 비상 상황에 대비하는 작업이고 개별 레코드의 변경을 추적하고 관리하는 데에는 적합하지 않습니다.

물론 DBMS 를 archiving mode 로 설정하고 운영하면 레코드 단위의 변경을 추적할 수도 있지만 비상시 복구용이라 DBA 의 도움과 시스템 차원의 작업이 필요하므로 레코드 단위 실수 방지는 app 레벨에서 대응하는 게 적절합니다.


삭제 실수는 일단 soft delete 기능을 적용하면 되지만 실수로 잘못된 데이터로 업데이트하는 것은 변경 이력을 별도로 추적하는 기능을 구현해야 합니다.

이런 요구 사항이 다른 곳에도 많은지 라라벨에서 쉽게 모델의 변경 이력을 관리해주는  Revisionable 이라는 패키지와  laravel-auditing 이라는 패키지가 github 에 올라와 있고 상당히 많은 star 를 받고 있습니다.

별점은  Revisionable 이 좀 더 많지만 저는 매뉴얼이 체계적으로 되어 있고 한 번에 여러 개의 컬럼이 변경되도 한 건의 이력 레코드만 생기는 장점이 있는  laravel-auditing 을 사용하고 있는데 간략한 사용법을 공유해 봅니다.

이현석님이 라라벨 일일 일식에 모델 변경 이력을 자동을 저장해주는 패키지 Revisionable 라는 유용한 글을 올려주셨으니 참고하세요.


설치

composer 로 패키지를 설치합니다.

composer require owen-it/laravel-auditing
BASH


config/app.php 에 프로바이더를 등록합니다.

'providers' => [
    // ...

    OwenIt\Auditing\AuditingServiceProvider::class,

    // ...
],
PHP


vendor 파일을 퍼블리싱합니다.

php artisan vendor:publish --provider "OwenIt\Auditing\AuditingServiceProvider" --tag="config"
BASH


config/audit.php 에서 auditing 패키지의 동작을 설정할 수 있습니다.

<?php

return [
	// auditing 동작 여부를 설정합니다.
    'enabled' => env('AUDITING_ENABLED', true),

	// User, IP Address 정보등을 가져올 리졸버를 설정합니다.
   'resolver' => [
        'user'       => OwenIt\Auditing\Resolvers\UserResolver::class,
        'ip_address' => OwenIt\Auditing\Resolvers\IpAddressResolver::class,
        'user_agent' => OwenIt\Auditing\Resolvers\UserAgentResolver::class,
        'url'        => OwenIt\Auditing\Resolvers\UrlResolver::class,
    ],
	// Audit 을 할 event 를 지정합니다. 기본적으로 생성, 갱신, 삭제, 복구시 동작합니다. 
	'events' => [
	    'created',
        'updated',
        'deleted',
        'restored',
    ],
PHP

DB migration 파일을 퍼블리싱합니다.

php artisan vendor:publish --provider "OwenIt\Auditing\AuditingServiceProvider" --tag="migrations"
BASH

audit 컬럼을 text 에서 json 으로 바꾸면 JSON Where 구문을 사용할 수 있으므로 생성된 migration 파일(날자_create_audits_table.php)에서 다음 내용을 수정합니다.

create_audits_table.php

// $table->text('old_values')->nullable();
// $table->text('new_values')->nullable();

$table->json('old_values')->nullable();
$table->json('new_values')->nullable();
PHP

DB migration 을 실행하면 Model 의 변경 사항을 기록하는 audits 테이블이 생성됩니다.

php artisan migrate
BASH

Model 설정


변경을 추적하려는 Eloquent Model 에 implements \OwenIt\Auditing\Contracts\Auditable  를 추가하고 Auditable trait 을 사용하도록 설정합니다.

아래의 예제는 고객 정보를 담고 있는 Customer 모델의 변경 사항을 추적합니다.

class Customer extends Model implements \OwenIt\Auditing\Contracts\Auditable
{
    use \OwenIt\Auditing\Auditable;

PHP

이제 Customer model 을 생성/수정/삭제/복구 하는 모든 event 마다 audits 테이블에 변경 사항이 기록됩니다.

Audit 레코드 사용

레코드 가져오기

audit 레코드에 접근하려면 먼저 audit 이 저장된 모델을 가져옵니다.

// Customer id
$c = Customer::find(1);

// 해당 모델의 모든 이력들
$audits = $c->audits;

// 첫 번째 이력
$f = $audits->first();

// 마지막 이력
$f = $c->audits()->latest()->first();

// id 로 가져오기
$f = $c->audits()->find(2);
PHP


eager loading 으로 User 모델 가져오기

laravel-auditing 은 자동으로 모델을 변경한 사용자 정보를 레코드에 추가하므로 아래와 같이 사용자 정보를 가져올 수 있습니다.

// audit 이 저장된 고객 id
$c = Customer::find(1);

// 해당 모델의 모든 이력들
$a = $c->audits->first();

// 수정한 사용자 정보 가져옴.
$a->user;
PHP


하지만 $a→user 를 호출하는 시점에 다시 DB fetch 가 일어나므로 N + 1 문제가 발생하고 성능에 악영향을 주게 되므로 다음과 같이 with() 메서드를 사용해서 이거 로딩으로 수정한 사용자 정보를 같이 가져올 수 있습니다.

$c = Customer::find(1);

$audits = $c->audits()->with('user')->get();
PHP


audit 메타 데이타 가져오기

audit 과 관련한 메타 데이타를 array 로 가져오려면 audit 모델의 getMetadata 메서드를 호출하면 됩니다.

$audits = Customer::find(1)->audits;

$f = $audits->first();

var_dump($f->getMetadata());
PHP


변경된 properties 만 가져오기

audit 모델의 getModified 메서드를 호출하면 audit 테이블의 old_valuesnew_values 만 가져올 수 있습니다.

$audits = Customer::find(1)->audits;
$f = $audits->first();
var_dump($f->getModified ());
PHP


Ref