라라벨 Livewire 4, 글 삭제 기능 구현 및 Policy 권한 제어

포스트 썸네일 이미지

이번 포스트에서는 드디어 Livewire 기반 CRUD의 마지막 기능인 삭제Delete 기능을 구현해 보겠다.

그리고 동시에 전에 언급한 남의 글을 수정하거나 삭제할 수 있는 문제도 해결해 보겠다.


이런 기능들은 전에도 이미 다뤘었다.

다른 점은 이전에는 Livewire를 사용하지 않았었는데, 지금은 Livewire를 사용하고 있다는 것뿐이다.




이전 포스트




전체적인 코드 수정은 이 깃허브 링크들을 통해서 확인할 수 있다.

보기 편하게 두 번으로 나눠서 커밋했다.




이건 뒤늦게 수정한 코드다.

아래에 설명도 추가해 뒀으니 참고하도록 하자.




글 삭제 기능 구현


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 파일에 코드를 추가하면 된다.




그 방법은 이 포스트 혹은 상단의 깃허브 링크를 참고하자.





다른 사용자의 글을 수정이나 삭제할 수 있었던 문제


이제 다른 사용자의 글을 수정하거나 삭제할 수 있었던 문제를 해결할 차례다.




이전에 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의 모든 기능이 완성되었다.

앞으로도 많은 기능을 구현해야 하는데, 뭐부터 다뤄야 할지 모르겠다.

주말 동안 고민 좀 해봐야겠다.




다음 포스트

이 글이 도움이 됐거나 유익했다면 스크롤을 조금만 더 내려서 댓글을 남겨주세요. (비로그인도 가능합니다!)
응원이나 피드백이 담긴 댓글은 제가 계속 블로그를 해나갈 수 있는 원동력이 됩니다. 😊

지인에게 보여주고 싶은 글이었다면 URL을 복사해서 메신저나 소셜 미디어에 공유해 주세요.
이전 포스트 다음 포스트

댓글 쓰기

0 Comments

문의하기 양식