이번 포스트에서는 드디어 Livewire 기반 CRUD의 마지막 기능인 삭제Delete 기능을 구현해 보겠다.
그리고 동시에 전에 언급한 남의 글을 수정하거나 삭제할 수 있는 문제도 해결해 보겠다.
이런 기능들은 전에도 이미 다뤘었다.
다른 점은 이전에는 Livewire를 사용하지 않았었는데, 지금은 Livewire를 사용하고 있다는 것뿐이다.
이전 포스트
라라벨 Blade 컴포넌트 동적 태그 변환, 버튼 컴포넌트에 href 속성을 넣으면 a 태그로 렌더링
글 삭제 기능 구현 · 4th-avenue/laravel13-livewire@6ac92e5 · GitHub
다른 사용자의 글을 수정이나 삭제할 수 있었던 문제 해결 · 4th-avenue/laravel13-livewire@01a2162 · GitHub
전체적인 코드 수정은 이 깃허브 링크들을 통해서 확인할 수 있다.
보기 편하게 두 번으로 나눠서 커밋했다.
filament 관리자 메뉴에서 사라진 Article 표시하기 · 4th-avenue/laravel13-livewire@db2a448 · GitHub
이건 뒤늦게 수정한 코드다.
아래에 설명도 추가해 뒀으니 참고하도록 하자.
글 삭제 기능 구현
resources/views/pages/articles/⚡show/show.php
public function deleteArticle()
{
if (Auth::check()) {
$this->article->delete();
} else {
return $this->redirectRoute('login', navigate: true);
}
return $this->redirectRoute('articles.index', navigate: true);
}
글을 삭제하는 deleteArticle 메서드를 만들었다.
물론 메서드의 이름은 자유롭게 다른 이름으로 바꿔도 상관없다.
if (Auth::check()): 현재 사용자가 로그인된 상태인지 확인한다.Auth를 임포트Import 하는 것을 잊지 말자.$this->article->delete();: 로그인 상태라면 현재 컴포넌트에 바인딩된 게시글(DB 레코드)을 삭제한다.return $this->redirectRoute('login', navigate: true);: 비로그인 상태라면 로그인 페이지로 리다이렉트 한다.return $this->redirectRoute('articles.index', navigate: true);: 삭제가 완료된 후에는 게시글 목록 페이지로 리다이렉트 한다.
참고로 현재의 이 코드들은 완성형이 아니다.
나중에는 필요가 없어서 뺄 코드들이 있다.
일단 계속해서 글 삭제 기능을 구현해 보겠다.
개별글 페이지에 글 삭제 버튼 생성 (wire:click과 wire:confirm)
resources/views/pages/articles/⚡show.blade.php
<div class="flex items-center justify-end mt-3 space-x-2">
<x-primary-button :href="route('articles.edit', $article)" wire:navigate>
{{ __('Edit') }}
</x-primary-button>
<x-danger-button wire:click="deleteArticle" wire:confirm="정말로 이 글을 삭제하시겠습니까?">
{{ __('Delete') }}
</x-danger-button>
</div>
글 수정 버튼 옆에 삭제 버튼을 만들었다.
space-x-2: tailwindcss 클래스다. 자식 요소(수정 버튼과 삭제 버튼) 사이의 가로 간격(Margin)을 자동으로 벌려주는 역할을 한다. 버튼마다 일일이ml-2또는mr-2같은 마진 클래스를 줄 필요 없이, 부모 요소에서 자식 간 간격을 한 번에 깔끔하게 제어할 수 있다.wire:click="deleteArticle": Livewire 이벤트 바인딩이다. 삭제 버튼을 클릭했을 때 백엔드 php 컴포넌트 클래스의deleteArticle()메서드를 비동기(AJAX)로 실행한다.wire:confirm="...": 실수로 삭제 버튼을 누르는 것을 방지하기 위해 서버 요청을 보내기 전 브라우저의 기본 확인(Confirm) 대화상자를 띄워준다. 여기서는 클릭하면 "정말로 이 글을 삭제하시겠습니까?"라는 팝업창이 나타나고, '확인'을 누르면 deleteArticle 메서드가 실행된다.
개별글 페이지에 글 삭제 버튼이 생겼다.
이 캡처 이미지처럼 버튼을 한글화를 하기 위해서는 lang/ko.json 파일에 코드를 추가하면 된다.
Laravel 회원가입 폼 수정: 아이디와 닉네임 필드 추가 및 한글화
그 방법은 이 포스트 혹은 상단의 깃허브 링크를 참고하자.
다른 사용자의 글을 수정이나 삭제할 수 있었던 문제
이제 다른 사용자의 글을 수정하거나 삭제할 수 있었던 문제를 해결할 차례다.
라라벨 Policy로 다른 사용자의 글 수정 및 삭제 차단하기
이전에 Livewire는 사용하지 않고 Laravel만으로 CRUD 기능을 완성했을 때도 마지막에 다른 사용자의 글을 수정하거나 삭제할 수 있었던 문제가 있었다.
이 포스트는 그 문제를 해결했던 포스트인데, 그때 사용한 방법을 그대로 여기에 적용하면 된다.
위의 포스트에서 이미 설명했던 내용들은 생략하고, 조금 달라진 점들만 설명하고 넘어가겠다.
make bash #컨테이너 안으로 들어간다.
php artisan make:policy ArticlePolicy --model=Article
VSCode의 터미널에서 이 명령어들을 실행한다.
그러면 app/Policies 폴더에 ArticlePolicy.php 파일이 생성된다.
이전에도 설명했지만, Policy 파일은 특정 사용자에게 특정 권한을 부여하기 위해 사용된다.
update와 delete 메서드는 각각 사용자가 게시글을 수정하거나 삭제할 권한이 있는지 확인한다.
각 메서드는 $user와 $article 객체를 인수로 받고, 내부의 로직이 true를 반환하면 사용자에게 해당 글의 수정이나 삭제 권한을 부여하고, false를 반환하면 권한을 얻을 수 없게 된다.
ArticlePolicy.php에 추가할 코드
class ArticlePolicy
{
public function viewAny(User $user): bool
{
return true;
}
// 생략
public function update(User $user, Article $article): bool
{
return $user->id === $article->user_id;
}
public function delete(User $user, Article $article): bool
{
return $user->id === $article->user_id;
}
// 생략
}
viewAny 메서드의 return false;를 삭제하고 return true;로,
그리고 update와 delete 메서드에는 return $user->id === $article->user_id;를 넣자.
viewAny 메서드를 return true;로 설정하지 않으면, filament 관리자 메뉴에서 Article이 사라지게 된다.
filament는 라라벨의 Policy 파일이 존재하면 이를 자동으로 감지하여 메뉴를 띄우기 전에 정책 파일의 viewAny 메서드를 검사하기 때문이다.
edit.php의 mount와 save 메서드에 추가할 코드
Gate::authorize('update', $this->article);
mount 메서드와 save 메서드 내부의 가장 상단에 이 코드를 추가한다.
이전과 약간 다른 점이 있다면, $this->가 추가된 것뿐이다.
컨트롤러를 사용했을 때(Livewire를 사용하지 않았을 때)는 메서드가 실행될 때 파라미터(매개변수)로 $article을 직접 받아서 쓰기 때문에 그냥 $article을 사용했었다.
그런데 Livewire에서는 메서드가 파라미터를 받는 대신 클래스에 미리 저장해 둔 $article 프로퍼티를 꺼내 써야 하므로 $this->article이라고 써야 한다.
save 메서드에 이 코드를 넣으면 권한이 없는 사용자가 글의 수정 버튼을 클릭하면 403 에러가 뜨게 되고, mount 메서드에 이 코드를 넣으면 권한이 없는 사용자가 글 수정 페이지로 이동하는 것도 차단한다.
자세한 설명은 링크한 포스트에서 정리했었다.
show.php의 deleteArticle 메서드
public function deleteArticle()
{
Gate::authorize('delete', $this->article);
$this->article->delete();
return $this->redirectRoute('articles.index', navigate: true);
}
if문을 제거했다.
ArticlePolicy.php에서 삭제 권한을 정의하고, Gate::authorize('delete', $this->article);을 호출하면, 라라벨이 로그인 여부 확인과 작성자 본인 확인을 한 번에 처리하기 때문이다.
본인의 글에서만 수정・삭제 버튼이 보이도록 수정
resources/views/pages/articles/⚡show/show.blade.php
@canany(['update', 'delete'], $article)
<div class="flex items-center justify-end mt-3 space-x-2">
@can('update', $article)
<x-primary-button :href="route('articles.edit', $article)" wire:navigate>
{{ __('Edit') }}
</x-primary-button>
@endcan
@can('delete', $article)
<x-danger-button wire:click="deleteArticle" wire:confirm="정말로 이 글을 삭제하시겠습니까?">
{{ __('Delete') }}
</x-danger-button>
@endcan
</div>
@endcanany
글을 수정이나 삭제할 권한이 없는 사용자에게는 수정과 삭제 버튼이 아예 보이지 않도록 수정했다.
수정 버튼은 @can('update', $article)과 @endcan으로 감쌌고, 삭제 버튼은 @can('delete', $article)과 @endcan으로 감쌌다.
이 두 버튼을 감싸고 있는 <div> 태그는 또 @canany(['update', 'delete'], $article)로 감쌌다.
이제는 내가 쓴 글에서만 수정・삭제 버튼이 보이고, 다른 사람이 작성한 글에서는 보이지 않는다.
이것으로 Livewire를 활용한 기본적인 CRUD의 모든 기능이 완성되었다.
앞으로도 많은 기능을 구현해야 하는데, 뭐부터 다뤄야 할지 모르겠다.
주말 동안 고민 좀 해봐야겠다.
다음 포스트


0 Comments
댓글 쓰기
🔸 댓글은 블로그 운영자의 승인 후에 블로그에 표시됩니다.
🔸 비로그인 방문자 분께서는 '익명'보다 이름/URL로 댓글을 남겨주시면 감사하겠습니다. (URL은 생략 가능합니다.)
🔸 구글 로그인 방문자는 '알림 사용'에 체크를 하시면, 남겨주신 댓글에 대한 답글 알림을 메일로 받아볼 수 있습니다. 📩