Topic = 하나의 Activity에서 여러 Fragment를 사용하는 경우 ViewModel을 어떻게 작성하고 관리하면 좋을까
✔️ 간단하게 SharedViewModel을 사용하고 생긴 문제들을 먼저 적어보고 SharedViewModel 에 대해서 정리
Why - SharedViewModel은 만능이 아니다.
글 작성일 기준 마지막으로 진행했었던 프로젝트에서는 Single Activity Architecture를 적용하여 프로젝트를 진행하였고 비즈니스 로직과 Fragment 간의 데이터 이동을 관리하는 SharedViewModel 하나로 여러 Fragment에서 사용할 수 있도록 해주었는데 아래와 같은 불편함이 있었다.
코드 가독성 저하
여러 Fragment에서 공유하지 않는 로직인데도 하나의 ViewModel에 모여 있어 코드 전체적인 가독성이 떨어졌다.
유지 보수성 저하
게시글 이미지를 불러오는 로직 같은 경우 여러 Fragment에서 사용하는 로직인데, 특정 Fragment에는 다르게 작동하도록 하고 싶으면 별도로 로직을 분리해주어야 했다.
프로젝트 내에서는 게시글 이미지를 불러오는 로직에서 List형식의 여러 이미지를 가져오는 로직을 단일 Uri 이미지로 가져오는 로직으로 분리하지 않아서 단일 이미지를 필요로 하는 상황에서도 List 형태로 데이터를 가져와 ImageList[0] 으로 이미지를 가져와야 하는 불편함이 생겼다.
의존성 및 결합도 증가
위의 유지보수성 저하와 같이 여러 Fragment에서 SharedViewModel의 동일한 비즈니스 로직을 사용하고 있을 때 해당 로직을 변경할 경우 예기치 못한 오류가 생겨 로직을 분리해주어야 했다.
그렇다면 SharedViewModel은 언제 사용하면 좋을까?
해당 질문의 답을 얻기 위해서 Android Develpoers에서 AAC - ViewModel의 개념에 대해서 다시 한번 확인 해보았는데, ViewModel 개요가 시작되는 첫 소제목의 ViewModel의 이점에서 데이터 지속성을 위한 편리한 API를 제공한다는 내용을 확인할 수 있다.
비즈니스 로직을 관리하는 경우에는 SharedViewModel을 사용하는 방법이 좋지 않다는 것을 알게 되었고, 그렇다면 ViewModel의 비즈니스 로직을 관리하는 것 외에 다른 이점인 Data 관리에 SharedViewModel이 사용되는 방법을 찾아 보았다.
SharedViewModel
간단한 SharedViewModel 단면도
[ 개요 ]
앱 개발을 하다보면 다양한 요구 사항에 따라 여러 화면에서 동일한 데이터를 공유해야 하는 경우가 있다. 진행중인 프로젝트가 SAA ( Single Activity Architecture ) , MAA ( Multi Activity Architecture ) 각각에 따라 View의 Lifecycle에 맞게 데이터를 관리할 수 있도록 해주어야 한다.
ViewModel은 수명주기를 고려하여 UI 관련 데이터를 안정적으로 저장하고 관리할 수 있으며 SharedViewModel은 여러 Fragment가 동일한 ViewModel 인스턴스를 공유하여 데이터를 주고받을 수 있도록 하고, 이를 위해 SAA 방식으로 Activity 범위에서 ViewModel이 생성되어 여러 Fragment들이 이를 참조하여 데이터를 공유할 수 있도록 한다.
[ 장점 ]
간결함
여러 Fragment에서 동일한 ViewModel 인스턴스를 공유하므로, Fragment 간에 데이터를 쉽게 공유하고 전달할 수 있다.
상태 유지
SharedViewModel은 Activity의 생명주기에 종속되므로, Fragment 간의 전환 또는 구성 변경과 같은 상황에서도 데이터의 일관성과 상태를 유지할 수 있다. 이는 사용자 경험을 향상시키고 데이터의 유지보수성을 높인다.
[ Bundle, ResultListener 를 사용하는 방법보다 SharedViewModel을 통한 데이터 이동이 권장되는 이유 ]
데이터의 실시간 동기화
A Fragment에서 데이터를 업데이트하면 다른 Fragment 들도 동일한 데이터를 즉시 참조할 수 있다. 이는 데이터의 일관성과 실시간 동기화를 보장한다.
Bundle, ResultListener의 경우 Fragment의 생명주기와 데이터의 동기화를 직접 관리해야 하므로 코드의 복잡성이 증가할 수 있어 SharedViewModel을 통해 번거로운 데이터 전달과 동기화 작업을 피할 수 있다.
확장성과 유연성
SharedViewModel은여러 Fragment 간에 데이터를 공유하므로, 프로젝트의 규모가 커지거나 변경이 발생해도 유연하게 코드를 작성할 수 있다. 새로운 Fragment가 추가되어도 기존의 SharedViewModel에 접근하여 데이터를 사용할 수 있어 효율적인 처리가 가능하다.
단방향 통신 vs 양방향 통신
Bundle, ResultListener 를 통한 데이터 이동 시 양방향 통신이 이루어지고, SharedViewModel을 통한 데이터 이동 시 ViewModle을 통해서만 데이터가 업데이트되고 공유되므로 흐름이 단방향으로 유지된다. 이는 데이터의 일관성과 예측 가능성을 높여준다.
테스트 용이성
Bundle, ResultListener를 사용하여 데이터를 이동시키는 경우 Fragment 간의 상호작용을 테스트하기 위해 추가적인 객체나 관리 작업이 필요하지만 SharedViewModel을 사용하는 경우 Fragment와 VIewModel을 독립적으로 테스트할 수 있어 ViewModel의 동작을 테스트하는 데 집중할 수 있다.
[ 주의할 점 ]
여러 Fragment에서 공유되는 데이터 관리에 SharedViewModel을 사용할 때 ViewModel과 View 사이에 의존성과 결합도가 증가할 수 있고. 이를 위해 Fragment 간에 공유되는 데이터에서 필요한 데이터는 UseCase Repository와 같이 인터페이스를 Hilt 를 통해 의존성 주입으로 의존성과 결합도를 낮출 수 있다.
이때 DI 모듈 단일 인스턴스를 여러 ViewModel에서 공유하는 경우 @ActivityRetainedScoped 또는 @Singleton 을 사용하여 범위를 지정해주어야 한다.
[ 의존성 주입 X → View를 거쳐 데이터 전달 ] 이미지 출처 : https://medium.com/@wodbs135/view-lifecycle%EC%97%90-%EB%94%B0%EB%9D%BC-viewmodel-%EA%B0%84-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B3%B5%EC%9C%A0%ED%95%98%EA%B8%B0-c6d9dbf2682b
[ 의존성 주입 O → View를 통하지 않고 ViewModel 간 데이터 공유 ]
이미지 출처 : https://medium.com/@wodbs135/view-lifecycle%EC%97%90-%EB%94%B0%EB%9D%BC-viewmodel-%EA%B0%84-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B3%B5%EC%9C%A0%ED%95%98%EA%B8%B0-c6d9dbf2682b
[ A. 오늘 복습한 내용 / B. 다음에 학습할 내용 ]
A. ViewModel과 SharedViewModel 각각 어느 상황에서 사용하면 좋을지 학습
B. Hilt를 통한 의존성 주입
B. Activity, Fragment, ViewModel 등 앱 전반적인 컴포넌트 생명주기 자세하게 복습, 학습하기
[오류,에러 등등]
1. SharedViewModel에 대해 추가적으로 학습하면서 지난 프로젝트에 부족했던 부분, 구조적인 오류가 발생했던 부분 알게되었다
→ SharedViewModel의 데이터 이동 로직에 매개변수 등을 사용해서 메서드를 분리하지 않고 사용했던 문제, 하나의 ViewModel에 비즈니스 로직과 데이터 이동 로직이 모두 포함되었던 문제
해결방법
→ 각 Fragment 별로 데이터를 관리하는 ViewModel과 비즈니스 로직을 관리하는 ViewModel 2개로 분리하는 코드로 먼저 작성 후 정상적인 구조가 될 경우 Hilt를 통해 의존성 관리 리팩토링 예정.