[Flutter] 29. 모두의 블로그 앱 만들기 (7) - 게시글 삭제

김주희's avatar
Jun 10, 2025
[Flutter] 29. 모두의 블로그 앱 만들기 (7) - 게시글 삭제
게시글 삭제만을 위한 vm이 필요가 없다.
왜냐 게시글 삭제라는 화면이 없고 상세보기 화면에서 일어나는 일이기 때문
⇒ 디테일 페이지의 상태를 변경하는 것(상태를 없애면 된다)
 

통신

api 문서

notion image
 
notion image
 

post_repository

notion image
 

post_repository_test

통신 테스트
 
 

VM

post_detail_vm

상세보기 화면에서 삭제하는 것이므로 상세보기에서 만들면 된다.

통신

notion image
 

화면 이동

success가 true이면 삭제가 완료되었다는 의미이므로 detail 페이지가 필요 없다. 따라서 게시글 목록으로 이동하면 된다. 그러므로 pushNamed으로 list 페이지로 이동하지 않고 pop으로 detail 페이지를 날리면 된다.
notion image
 

VM 클래스 내부 build 함수에서 vm 파괴 확인하기

notion image
ㅋㅋ
 
detail 페이지(view)를 pop하게 되면 view는 사라지지만 vm은 살아 있으므로 pop될 때 view 뿐만 아니라 vm도 같이 삭제되도록 해야한다.
현재 build 함수는 init(postId); 로 상태를 초기화한 뒤, return null; 를 통해 상태 값을 세팅하도록 만들어져 있다. 상태 초기화와 상태 값 세팅 사이에 VM이 파괴되었는지 확인하는 이벤트를 걸 수 있다.
이 이벤트는 상태가 끝까지 떠있을 수 없으니 (모든 페이지와 상태가 다 떠있으면 느려져서 앱 터지는 문제가 생기기 때문) 언제 파괴되는지 알아야 한다.
vm이 파괴되는지 안되는지 볼 수 있는 ref.onDispose() 라는 함수가 있다. ref.onDispose()는 해당 Provider가 더 이상 사용되지 않을 때 (메모리에서 제거될 때) 실행할 정리 작업을 등록할 수 있는 함수로 즉 구독자가 더 이상 없어서 자동으로 메모리에서 해제될 때 실행되고 정리 작업을 하는 콜백 함수(보통 void Function())를 인자로 받는다. 함수의 인자에 콜백 함수로 익명 함수 () { Logger().d("PostDetailModel 파괴됨");} 를 전달하여 provider, 즉 VM이 파괴되면 파괴되었다고 로그를 남기도록 한다.
notion image
 

View에서 VM 호출하기

이제 post_detail_vm의 deleteOne이라는 함수를 detail 화면 view에서 호출해야한다. (vm은 무조건 view에서 호출한다.
 

provider에게 id 전달

postDetailProvider가 family로 선언된, 매개변수를 받는 provider이기 때문에 postId를 받아서 postId에 해당하는 상태만 관리하게 된다. id 인자를 전달받아서 ref.read(provider.notifier)로 id에 해당하는 provider 내부의 상태를 제어하는 객체인 PostDetailVM를 가져올 수 있다. 이때 객체는 singleton이기 때문에 같은 id일 경우 하나 더 만들어지지 않고 id가 달라지면 객체는 새로 만들어진다. (id별 singleton)
notion image
 
notion image
 

deleteOne 함수 호출

삭제 버튼을 클릭했을때 삭제가 되도록 onPressed 속성에서 id를 인자로 받는 vm의 deleteOne함수를 호출한다.
notion image
 

View rebuild

View 날릴 때 ViewModel도 같이 날리기

글 삭제 버튼을 클릭하면 지금까지의 코드로는 서버 상에 삭제는 되지만 리스트 화면에 반영이 안되어있는 상태이다. 여기서 몇 가지 방법이 있는데 먼저 첫 번째로는 목록을 드래그해서 새로고침하는 Pull to Refresh가 있다. 첫 번째 방법은 수동이므로 자동으로 새로고침되어서 반영이 되는 것이 좋다.
또한 지금 view가 날라갈 때 view model도 없어져야 하는데 view model이 파괴되는 이벤트가 뜨지 않았다. (작성해둔 파괴되었다는 내용의 로그가 안 떴음)
view가 없어질때 view model도 없어지도록 하는 방법은 post_detail_vm에서 창고 관리자를 만드는 NotifierProvider와 창고를 만드는 FamilyNotifier 앞에 AutoDispose를 붙여주면 된다. view를 없앨 때 view model도 같이 없애기 위해 AutoDispose는 웬만한 vm에는 다 건다.
notion image
 

전체 흐름 정리

삭제하기 버튼 클릭시 deleteone 함수가 호출 되고 pop 하면 화면이 메모리에서 소멸된다. ref.onDispose()는 provider(vm)가 dispose(없어지게) 될 때 콜백 함수를 실행하도록 콜백 함수를 등록만 하고 즉시 실행되지 않는다. 즉, 실제 콜백 실행은 vm이 dispose되는 상황에서 발생한다. dispose가 발생하는 시점은 상세보기 화면이 stack에서 제거되고 postDetailProvider에 대한 참조가 사라지면 AutoDisposeNotifierProvider에 의해 자동으로 dispose 되고 이때 ref.onDispose 콜백이 실행된다.
vm를 남겨둬야하는 특별한 상황 이외에는 view가 삭제되면 vm도 삭제한다. 즉 비지니스에 따라서 vm을 없앨지 남겨둘 지 결정하게 된다.
 
notion image
notion image
 

post_detail_vm에서 post_list_vm 사용하기 (vm끼리 협업)

이제 특정 게시글을 삭제하면 detail의 view, view model 그리고 list에서 특정 게시글을 날려야 되는데 이때 vm끼리 협업 가능하다.
deleteOne으로 삭제될 때 제일 쉬운 방법은 pop되기 전에 await를 걸고 list vm의 init()을 실행하는 것이다. 그럼 통신이 끝나면 pop 된다. 그러나 관리하기에는 단순하여 좋지만 통신이 많아지는 문제가 있다.
notion image
 
두 번째 방법은 통신하지 않고 상태의 (삭제하고자 하는 id의) 특정 게시글을 날리면 된다. 이는 java에서 stream api의 filter와 같은 방법으로 처리한다. chaining 거는 게 나중에는 복잡해져서 힘들기 때문에 그냥 프로젝트할 때는 init으로 초기화 해서 만들고 프로젝트가 끝나면 나중에 어디에 chain 걸려져 있는지, 이 view model에서 어떤 거 삭제하면 얘한테 연결되고 어떤 애한테 영향을 미치는 지 정리가 된다. 그러고 나서 상태 관리로 변경하는 것이 낫다. init을 호출하는 방식이 아니라 두 번째 방식을 쓰면 함수를 만들어야 하기 때문이다.
즉, 게시글 목록 vm에서 deleteOne 함수를 만든다. 그냥 deleteOne이라고 하면 통신하는 함수 같기 때문에 알림만 받는다는 의미를 담을 수 있도록 컨벤션으로 앞에 notify를 붙여준다. 따라서 notifyDeleteOne이라고 함수명을 지으면 어떤 vm이 다른 vm에게 상태가 변경되었다고 알림을 주는 것을 알 수 있다.
notion image
 

list vm의 notifyDeleteOne 함수

notifyDeleteOne 함수는 통신을 하지 않으므로 비동기일 수 없다. 따라서 Future<void>가 아닌 그냥 void를 반환 타입으로 가진다.
일단은 PostListModel 값인 state를 가져온다. PostListModel의 List<Post> posts에서 특정 id의 post만 없애면 된다. where은 java의 filter와 같고 true, false를 return한다. p.id가 postId와 같지 않은 것만 모으는 깊은 복사 코드이다. 따라서 기존의 post를 건드리지 않는다. (stream도 깊은 복사로 기존의 객체를 건들지 않는다.) state 전체가 posts가 아니므로 일단 model.posts에 담고 이를 copyWith해주면 된다. 게시글 쓰기의 경우에도 마찬가지로 list에 notify 해줘야 새로 등록된 게시글이 목록에서 보이게 된다.
notion image
 
지금은 list에만 알림을 주면 되지만 나중에는 여러 곳에 알림을 줘야 하는 경우도 있다. 이때는 이벤트 버스 클래스를 만들어서 그 안에 묶어서 세팅하면 된다. (패턴 공부하면 알 수 있다.)
 
 
 
 
Share article

jay0628