현재 진행중인 반려동물 기록 서비스 'ZOOC'을 개발핟면서 생긴 이슈다.
아래는 우리 앱의 메인 화면이다.
호스팅 액티비티에 바텀 네비게이션이 있고 홈, 마이페이지 두 개의 프레그먼트가 전환되는 형태이다.
가운데 + 버튼을 누르면 기록을 위한 새로운 액티비티가 생성되는데, 해당 액티비티를 갔다오면 홈 프레그먼트의 데이터를 리프레시해야했다.
그래서 Activity Result API를 이용해 기록 액티비티에서 돌아오는 시점을 알고 데이터를 리프레시 하려고했다.
그런데 여기서 문제가 발생했다. 바텀 네비게이션은 호스팅 액티비티에, 리프레시할 데이터는 홈 프레그먼트에 있었기 때문이다.
즉, 바텀 네비게이션이 액티비티에 있으므로 기록 액티비티에서 돌아오는 시점을 프레그먼트가 아닌 액티비티가 아는 것이 문제였다.
코드로 보자면 아래와 같다.
결론부터 말하자면 두 가지 방법이 있었다. 이제 그 방법들에 대해 이야기해보겠다.
Shared ViewModel
가장 먼저 떠오른 것은 AAC ViewModel을 활용한 데이터 공유였다.
홈과 마이페이지 모두 하나의 액티비티에 호스팅 되는 프레그먼트들이므로 이들의 생명주기는 호스팅 액티비티의 생명주기 내에 있다.
그러므로 호스팅 액티비티의 생명주기를 따라가는 뷰모델을 생성하고 사용하면 호스팅 액티비티 및 하위 프레그먼트들 끼리 데이터 공유가 가능해진다.
현재 프로젝트에서 AAC ViewModel을 사용하고 있기 때문에 뷰모델을 통한 데이터 공유가 가장 먼저 떠오른 것 같다.
하지만 이 방안은 별로 내키지 않았다.
단순히 기록 액티비티에서 돌아왔음만을 프레그먼트에 알리면 되는데 그것을 위해 새로운 뷰모델을 만들고 싶지 않았기 때문이다. (호스팅 액티비티의 스코프를 따라가는 뷰모델이 기존에 없었다)
그래서 다른 방안을 찾아보기 시작했다!
Fragment Result API
이번에 새로 알게 된 녀석이다. 공식문서가 궁금하다면 봐도 좋다!
이 녀석은 프레그먼트간, 상위 및 하위 프레그먼트 간, 호스팅 액티비티와 프레그먼트 간에 데이터 전달이 가능하다!
요점은 보내는 곳에서 setFragmentResult() 함수를 통해 결과를 지정하고 받는 곳에서 setFragmentResultListener() 함수를 호출해서 결과를 받는다는 것이다.
이제 필자가 직접 작성한 코드를 예시로 보자!
호스팅 액티비티(MainActivity)에서 setFragmentResult() 함수를 호출해 보내고 싶은 데이터를 담는다.
하위 프레그먼트인 HomeFragment에서는 결과를 받아올 리스너를 등록한다.
코드를 살펴보면 setFragmentResult() 함수를 호출할 때 어떤 프레그먼트로 보낼지에 대한 정보는 없다.
단지 supportFragmentManager에 전달할 뿐이다.
공식문서를 살펴보면 작동 방식을 대략적으로 생각해볼 수 있다.
- 프레그먼트가 STARTED 상태일 때만 결과 수신이 가능하다.
- 전달된 데이터(결과)는 우선 FragmentManager에 저장된다.
- 누군가 결과를 수신하면 FragmentManager에 저장된 결과는 삭제된다.
어떤 프레그먼트로 결과를 보내는지 설정하지는 않지만, 누군가 수신하면 결과는 삭제되므로 모든 프레그먼트가 수신할 수 있는 것은 아니다.
예를 들어 호스팅 액티비티에서 특정 값을 하위 프레그먼트들에 넘겨주고 싶다.
이때 Fragment Result API를 사용하면 하위에 있는 모든 프레그먼트들에게 값을 넘겨주지 못하고 가장 먼저 STARTED 상태가 된 하나의 프레그먼트만 값을 받을 수 있다는 것이다.
그러므로 이런 상황에서는 Shared ViewModel을 사용하는 편이 더욱 적절할 것이다.
추가적으로 아직 수신자가 데이터(결과)를 받지 못했을 때 같은 키로 setFragmentResult()를 한번 더 호출하게 된다면 나중에 주입된 결과 값으로 업데이트된다. (결과가 두 번 전송되지 않고 업데이트돼서 한 번 전송된다는 말이다)
마무리
필자는 Fragment Result API를 사용하는 것이 훨씬 용도에 알맞다고 생각돼서 Shared ViewModel이 아닌 Fragment Result API를 사용했다.
데이터를 계속해서 공유하는 것이 아니라, 특정 시점에 결과를 전송하는 작업이기에 Shared ViewModel은 맞지 않다고 판단했다.
이런 API가 존재한다는 것을 이번에 처음 알았는데, 이런 유용한 API가 존재했음을 몰랐다는 것이 굉장히 신기하다.
'트러블 슈팅' 카테고리의 다른 글
식은땀 나는 개발자로서의 첫 핫픽스 (절대 미들닷을 잊지마) (3) | 2024.03.29 |
---|---|
한글 변수는 나쁠까? (0) | 2024.02.12 |