이진욱
안녕하세요~ 경조사비 관리 기록 서비스 수수(SUSU) 안드로이드 개발을 맡은 이진욱입니다!
이번 메이커로그에서 수수 안드로이드 팀의 MVI 적용기를 공유하고자 합니다
MVI에 대해 간단히 설명하자면 …
Model :
앱의 상태를 나타냅니다. 상태는 단 하나만 존재해야해요.
아래 사진에서의 Model은 다음 버튼의 활성화 여부, 사용자가 입력한 연락처 로 볼 수 있어요
View :
눈에 보이는 화면이에요
JetPack Compose의 Composable 함수, Activity 생각하시면 됩니다 (쉽죠?)
Intent :
앱의 상태를 변경하려는 의도에요. 앱의 상태는 Intent를 통해서만 변경할 수 있어요.
위 사진에서 사용자가 타이핑을 하면 연락처가 입력되겠죠? 사용자가 타이핑한다 를 Intent로 생각하시면 됩니다~
그 외에 다음 버튼을 클릭한다 도 Intent에요
SideEffect :
상태를 변경할 필요가 없는 이벤트는 SideEffect라는 녀석으로 처리합니다.
아까 앱의 상태 (Model)가 다음 버튼의 활성화 여부, 사용자가 입력한 연락처 라고 말한거 기억나시나요?
그러면 화면 전환, 스낵바 노출 처럼 상태를 변경할 필요가 없는 이벤트는 어떻게 처리할까요? 이때 바로 SideEffect를 사용하면 됩니다
스낵바와 화면 전환을 앱의 상태로 볼 수도 있을까?
여기서 꼭 말하고 싶은게 하나 있어요.
화면 전환, 스낵바 노출 은 관점에 따라 앱의 상태 (Model)로 볼 수도 있어요
실제로 MVP Open source library Mosby 를 개발한 Hannes Dorfmann 은 화면 전환, 스낵바 노출을 상태 변경을 통해 처리했어요
추가로 Iveta Jurcikova도 상태로 보는 걸 추천하고있어요
Iveta Jurcikova이 작성한 글 일부를 덧붙일게요
내비게이션
MVI 아키텍처에서 내비게이션은 여전히 해결해야 할 문제입니다.
한 가지 접근 방식은 내비게이션을 상태의 일부로 간주하는 것입니다. 화면을 변경하고 싶다면 관련된 intent()를 트리거하고 전체 사이클을 거쳐야 하며, 상태의 값을 기반으로 화면을 변경할 수 있습니다.
또 다른 방법은 사이드 이펙트의 결과로 내비게이션을 호출하는 것입니다. 이 방법은 좋아 보이지만, 사이드 이펙트는 주로 API 호출, 데이터베이스 작업 또는 원격 로깅과 관련이 있으며, 이러한 개념들을 혼합하는 것은 권장하지 않습니다.
제가 제안하는 것은 직접 내비게이션을 호출하는 것입니다. 저는 우리의 상태가 화면에 표시하고 싶은 데이터를 보유해야 하며, 어떤 화면을 표시할지는 상태가 아니라 화면 자체가 결정해야 한다고 생각합니다. 특히 이제 Android Jetpack의 Navigation Component를 활용할 수 있게 되었기 때문에 더욱 그렇습니다. 적어도 저에게는 그렇습니다.
반면, Orbit 공식 문서의 예시 코드를 보면 Snackbar(toast)를 SideEffect로 보고있는 것 같네요
// https://orbit-mvi.org/
private fun handleSideEffect(sideEffect: CalculatorSideEffect) {
when (sideEffect) {
is CalculatorSideEffect.Toast -> toast(sideEffect.text)
}
}저는화면 전환이나 스낵바 노출과 같은 동작을 상태로 처리하게 되면 더 복잡해진다고 판단했어요.
Hannes Dorfmann의 접근 방식(링크, 링크)에서 isPullToRefreshError 상태일 때 스낵바를 표시하도록 처리하는데요. (모든 내용을 설명하면 너무 길어지기 때문에 첨부한 링크를 보고 오시는걸 추천해요!)
여기서 isPullToRefreshError는 스낵바 노출 여부를 직접적으로 알려주지 않기 때문에, 이를 이해하기 위해서는 코드의 다른 부분을 참조해야해요.
또한, 스낵바 같은 요소는 특정 시간이 지나면 자동으로 사라지기 때문에, 이를 상태로 관리하기 위해서는 사라지는 시점을 관리하는 추가적인 로직이 필요해요. 이런 점들이 오히려 더 복잡하다고 생각했어요.
그래서 수수에서는 SideEffect로 처리했습니다.
그래서 MVI 어땠는데?
상태가 Activity, ViewModel 등 여러 곳에 흩어져 있지 않고 중앙에서 관리되어 관리가 편리했습니다.
또 데이터의 흐름이 단방향으로 흐르니 로직 예측이 쉬웠습니다. (뻔한 장점이지만 정말 그렇더라구요 몸소 체감하니까 다른 느낌이었습니다)
하지만 … MVI … 역시 만만치 않게 번거로웠고 시간 압박도 느꼈습니다.
(수수를 개발할 당시에는 시간이 넉넉하지 않아서 MVI를 선택한 걸 조금 후회 했었습니다 … ㅎㅎ)
저는 시간이 어느정도 지난 뒤, 다시 코드를 봤을때 알아보기 좋아야 좋은 코드라고 생각하는데요.
MVI 적용 후 1~2달 뒤에 기능 및 버그 수정을 해봤을때 아주 만족스러웠어요!
MVI 써볼까 말까 고민이시라면 꼭 써보시길 추천하며 글 마무리합니다 ㅎㅎ ~
수수 SUSU
경조사비 관리 기록 장부 서비스