소프트 삭제시 연관 모델도 소프트 삭제하기 쉽게해주는 패키지, 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




유료 구독자 전용 레터입니다.

한 달 1만원으로 매일 라라벨 관련 메일 받아보시고 과거 메일도 열람하세요. 일반 구독으로 공개글만 받아보실 수도 있습니다.

구독하기 버튼을 눌러주시면 구독과 동시에 xly에도 가입됩니다.

이현석

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