소프트 삭제시 연관 모델도 소프트 삭제하기 쉽게해주는 패키지, laravel-soft-cascade free

2019-07-22

소프트 삭제


데이터베이스의 데이터는 지워지고 나면 끝이다. 되돌릴 수 없다. 그래서 데이터베이스에서 실제로 지우지는 않고 지운 것 처럼 처리하는 방법을 종종 사용한다. 벨에서는 소프트 삭제를 사용하면 된다.


외래키 제한


User 1이 Post 1을 작성하면 posts 테이블에 user_id가 1로 기록된다. user_id를 흔히 외래키(Foreign Key, FK)고 부른다. 그런데 만약 User 1이 삭제 된다면? posts 테이블의 Post 1은 존재하지 않는 user_id를 참조하게 된다. 이런 경우를 참조 무결성이 깨진다고 한다. 그래서 참조 무결성이 깨지지 않도록 강제하기 위해 외래키에 제한을 걸기도 한다.


벨에서는 마이그레이션을 작성할 때 외래키 제한을 할 수 있는 기능을 제공한다.


Schema::table('posts', function (Blueprint $table) {
$table->unsignedBigInteger('user_id');

$table->foreign('user_id')->references('id')->on('users');
});

위와 같이 외래키 제한이 걸리면 Post 1이 남아있는 한 User 1을 삭제할 수 없다. User 1이 지워져서 참조 무결성이 깨지는 걸 막는 방이다. 반면 아래와 같이 삭제에 cascade 옵션을 주면 User 1을 지울 때 User 1을 참조하는 것들을 모두 지움으로써 참조 무결성을 유지한다.


Schema::table('posts', function (Blueprint $table) {
$table->unsignedBigInteger('user_id');

$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});

즉, 위와 같은 상황에서 User 1을 삭제하면 Post 1도 함께 지워진다.


소프트 삭제와 외래키 제한


벨은 기본적으로 소프트 삭제시 cascade 옵션의 외래키 제한이 적용되지 않는다. 왜냐하면 소프트 삭제는 데이터를 삭제하는게 아니고 deleted_at에 삭제 시점을 업데이트 하기 때문이다. deleted_at 컬럼의 값을 기반으로 ‘소프트웨어적으로’ 삭제된 데이터인지 여부를 판단한다. 외래키 제한은 데이터베이스에서 지원하는 기능이기 때문에 실제로 데이터베이스에서 삭제 행위가 있어야 적용된다.


서 laravel-soft-cascade 패키지의 저자는 소프트 삭제를 사용하는 경우에는 참조 무결성을 유지하기 위해 수작업이 필요했다고 한다. 아래와 같이 모델의 boot() 메소드를 활용할 수도 있고,


public static function boot()
{
parent::boot();

static::deleted(function($user)
{
$user->posts()->delete();
});
}

좀 더 나은 방법으로 모델 이벤트를 활용할 수도 있다고 했다.


User::deleted(function($user) {
$user->posts()->delete();
});

User::restored(function($user) {
$user()->posts()->withTrashed()->restore();
});

laravel-soft-cascade 패키지를 사용하면 다음과 같이 할 수 있다고 한다.


<?php
namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
use \Illuminate\Database\Eloquent\SoftDeletes;
use \Askedio\SoftCascade\Traits\SoftCascadeTrait;

protected $softCascade = ['posts', 'comments'];

public function posts()
{
return $this->hasMany('App\Post');
}

public function comments()
{
return $this->hasMany('App\Comment');
}
}

\Askedio\SoftCascade\Traits\SoftCascadeTrait 트레이트를 추가하고 $softCascade에 함께 처리할 관계를 배열로 넘겨주면 된다. 직접 모델의 boot() 메서드를 오버이딩하거나 모델 이벤트를 사용하는 것보다 훨씬 간편해보인다.

===

1 1

2019년 7월 22





이현석

바쁜 팀장님 대신 알려주는 신입 PHP 개발자 안내서를 쓰고, 클린 아키텍처 인 PHP를 번역했습니다. 2020년에 출간될 Laravel Up & Running 2nd Edition을 번역하고 있습니다.