Android 구조 개편기 기본구조 -> MVVM → 클린아키텍처 + 멀티모듈 → Compose + MVI 현업 적용기 (마지막 Compose + MVI 편)

2025. 5. 3. 19:51Android

반응형

안녕하세요, 구구집사입니다.

이번 글은 Android 구조 개편기 시리즈의 마지막 편, Compose + MVI 현업 적용기 입니다.

 

1부에서는 MVVM으로의 전환기, 2부에서는 클린 아키텍처와 멀티모듈화 과정을 다뤘습니다.


이번 글에서는 Jetpack Compose 도입 이유부터, 기존 양방향 구조의 한계를 넘어서기 위한 MVI 도입기

 

그리고 도입 후의 저의 생각 경험까지 공유드리겠습니다.

 

 

1. XML에서 Compose로 전환하게 된 이유

기존 프로젝트는 오랫동안 XML 기반 View 시스템을 사용해 왔습니다. 하지만 아래와 같은 이유로 Compose 도입을 검토하게 되었습니다.

  • 오랫동안 XML을 사용해 왔기에 Compose 도입이 필요할까 고민했지만,
    구글이 공식적으로 적극 지원하고 있다는 점,
    그리고 이미 많은 기업에서 실무에 도입하고 있다는 트렌드를 무시할 수 없어
    점진적으로 Compose를 적용하게 되었습니다.

  • 새로운 기술을 도입하는 데 따른 학습 비용과 리스크가 고민됐지만,
    팀원들이 학습할 가치가 충분하다고 판단했고,
    유지보수 측면에서도 Compose가 더 단순해질 것으로 기대되어
    실험적으로 일부 화면부터 도입하게 되었습니다.

  • 테스트 코드 작성이 훨씬 수월해진다는 점도 Compose 도입을 결정하는 데 큰 요인이었습니다.
    XML 기반의 UI는 테스트하려면 직접 빌드 하여
    UI 상태 변화도 눈으로 직접 확인해야 할 일이 많았지만,
    Compose는 컴포저블 단위로 UI를 분리할 수 있어서
    단위 테스트, 프리뷰 확인, 재사용성 측면에서 훨씬 효율적이었습니다.

이 시점에서 iOS 팀도 SwiftUI 도입을 결정하게 되었고, 다음과 같은 공통 배경이 있었습니다:

  • 양 플랫폼 모두 선언형 UI로의 흐름을 따르기 시작함
  • 복잡한 상태 관리보다 선언형에서의 간결한 표현이 유지보수에 효과적
  • 코드 기반 UI 작성으로 생산성과 테스트성 향상

Android도 Compose로 방향성을 잡으며, 양 플랫폼 간의 개발 패턴 유사성이 생겨 공통 기획 및 설계의 효율도 함께 개선되었습니다.

 

 

2. Compose UI의 장점과 단점

 

xml -> Compose 를 사용 하면서 장점과 단점을 설명하고자 합니다.

 

일단 제가 느꼇을때의 장점

 

장점

  • UI 만들 때 머릿속이 덜 복잡해짐
    XML 쓸 땐 레이아웃 파일 열고, ID 달고 ViewBinding 연결하고,
    상태 바뀌면 visibility 조작하고 notifyDatasetChanged 쓰고… 이런 게 너무 반복적이었습니다.
    Compose는 그냥 상태만 잘 관리하면 알아서 뷰가 바뀌니까,
    “아 이거 클릭하면 이 UI는 사라지고, 저건 보이게 하자” 라는 생각을 코드로 바로 옮길 수 있었어요.

  • 레이아웃 파일 열고 닫을 필요가 없음 
    XML - ViewModel - Adapter - Fragment 왔다갔다 하던 손이 Compose에선 확 줄었어요.
    화면 구성을 한 파일 안에서 쭉 짤 수 있다는 게 진짜 크고,
    컴포저블로 분리해두면 다른 화면에서도 재활용이 쉬워서 생산성 확 올라갑니다.

  • UI 확인이 빠르다
    AndroidStudio 업데이트를 해서 그런지 USB 디버깅 연결한 상태에서
    Compose UI 를 바꾸면  XML처럼 전체 빌드 안 해도 되고,
    바로바로 디바이스에서도 바뀌어서 편했습니다.

단점

  • 처음엔 뭐가 뭔지 하나도 모르겠음
    선언형 UI가 좋다고는 하는데… 처음엔 그 선언이 뭔지도 몰랐어요.
    remember, mutableStateOf, LaunchedEffect 같은 단어들이 처음엔 다 낯설고,
    xml 로 짜다가 Compose 로 짜다보니 먼가 HTML 짜는 느낌이라 버벅거리게 되더라고요.

  • 빌드 시간이 너무 길어질 때
    특히 프로젝트 규모가 커지면 Compose Preview나 빌드 캐시 날릴 때 엄청 느려져요.
    “이거 수정한 건데 왜 이렇게 오래 걸리지?” 싶은 순간이 꽤 많았어요.
    Compose는 느릴 땐 정말 답답하게 느려요.

  • Preview 여러개 하면 Android Studio 느려짐
    저만 체감 한거일수도 있는거지만
    xml 로 UI 짤때는 한번도 경험해보지 못한
    Preview 를 사용하니 Android Studio 가 간혈적으로 강제 종료가 되더군요..
    Preview 4개정도 띄어 놓긴했는데 그래도 제가 현재 사용하고있는 맥북에어 m3 에 메모리 24gb 인데 그렇더군요
    맥북 스팩이 딸리다고는 생각안하고 아직까지는 Preview 같은경우 좀 최적화가 필요 해보입니다.

 

 

3. Compose 사용 중 발견한 양방향 통신의 불편함

Jetpack Compose로 UI를 전환하면서 기존에 사용하던 MVVM 구조를 그대로 유지했습니다. XML 기반 개발에 익숙했던 저에게 처음 Compose는 상태 기반 UI의 장점만큼이나 몇 가지 불편한 점도 함께 느껴졌습니다.

 

가장 먼저 부딪힌 것은 데이터 흐름의 불명확함이었습니다.

 

  • 기존 XML에서는 View → ViewModel → LiveData → View 업데이트 흐름이 어느 정도 직관적이었고, 특히 onClick과 같은 명시적인 이벤트 처리 덕분에 사용자 액션의 발생 지점을 빠르게 파악할 수 있었습니다. 하지만 Compose에서는 Modifier.clickable이 컴포저블 여러 곳에 분산되어 사용되다 보니, 특정 이벤트가 어디서 발생했는지 추적하기가 오히려 더 어려워졌습니다.

  • Activity나 Fragment에서 직접 observe()를 사용해 UI를 갱신하던 방식과 달리, Compose에서는 Composable 내부에서 상태만을 기반으로 UI를 그리도록 권장하다 보니, 뷰모델과 UI 간에 단방향 흐름이 강제되는 구조가 답답하게 느껴지기도 했습니다. 특히 비즈니스 로직과 UI 로직이 얽히는 경우, 어디서 상태를 바꾸고 있는지 파악이 쉽지 않아 디버깅에 시간이 더 걸리는 경험도 있었습니다.

Compose가 추구하는 선언적 UI의 철학은 분명 매력적일순 있지만, 기존 XML 방식의 명시적이고 절차적인 코드에 익숙한 개발자에게는 초반 진입장벽이 다소 존재한다고 느꼈습니다.

 

4. 단방향 데이터 흐름을 위한 MVI 도입

앞서 언급한 양방향 통신의 불편함을 경험한 뒤, 이 문제를 어떻게 풀어야 할까 고민이 많았습니다.
Compose 자체는 선언적 UI를 기반으로 하다 보니, 기존 XML 기반 MVVM 구조와는 여러모로 맞지 않는 부분이 많았기 때문입니다.

"좋은 방법이 없을까?" 하는 마음에 여러 아키텍처를 다시 살펴보던 중, 과거에 공부했던 MVI(Model-View-Intent) 패턴이 문득 떠올랐습니다.
처음에는 단순한 아이디어였지만, 찾아볼수록 이 구조가 Jetpack Compose의 단방향 데이터 흐름 철학과 매우 잘 어울린다는 것을 깨닫게 되었습니다.

MVI가 Compose에 잘 맞는 이유

기존 MVVM에서는 View와 ViewModel 사이에 데이터가 서로 주고받는 구조다 보니,
UI 갱신 시점이나 상태 변화의 흐름을 정확히 파악하기가 쉽지 않았습니다.

혼자 개발할 때는 어느 정도 컨트롤이 가능했지만, 여러 명이 함께 작업하는 팀 개발 환경에서는
"이 상태가 어디서 바뀌었지?", "이 이벤트는 누가 발생시킨 거지?" 하는 혼란이 자주 생겼습니다.

반면, MVI는 모든 흐름이 한 방향으로만 흘러가도록 설계되어 있어
상태 추적이 훨씬 명확해지고, 디버깅 또한 직관적으로 진행할 수 있었습니다.

무엇보다 상태(State)와 사용자 이벤트(Intent), 그리고 일회성 동작(SideEffect)을 명확히 분리할 수 있어,
코드의 가독성과 유지보수성 측면에서도 큰 도움이 되었습니다.

 

5. 실제 MVI 설계 방향

 

  • 단방향 흐름 유지
    사용자의 행동(Intent)은 ViewModel로 전달되고, ViewModel은 이를 처리한 후 새로운 State를 생성합니다.
    Compose는 이 State를 기반으로 Composable을 자동으로 재구성합니다.

  • UI 책임 분리를 위한 UiState, UiEvent
    Compose에서는 화면이 상태(State)에 따라 자동으로 그려지기 때문에
    "화면이 어떤 상태인지"와 "무슨 일이 일어났는지"를 분리해서 관리하는 게 중요하게 생각했습니다.
    그래서 저는 화면 정보를 UiState로, 클릭이나 네비게이션 같은 한 번만 발생하는 동작은 UiEvent로 나눠 관리했습니다.
    이렇게 나누니 코드 흐름이 훨씬 명확해졌고, 어떤 데이터가 UI를 바꾸고 어떤 이벤트가 처리되어야 하는지 한눈에 파악할 수 있었습니다.
  • Composable은 오직 상태만을 그린다
    Composable은 더 이상 로직을 직접 다루지 않고, 오직 State를 기반으로 UI만 렌더링하게 되었습니다.
    이로 인해 코드가 선언적이고, 테스트도 용이한 구조로 개선되었습니다.

출처 - 구구집사

 

이게 실제 설계한 구조입니다.

지난번 포스팅에서 기존 클린아키텍처에서 Presentation Layer 만 변경 된것을 확인 할수있습니다.

 

5. MVI 도입 후의 장점과 단점

장점

  • UI 상태 예측 가능
    어떤 Intent(Event)가 어떤 State를 만드는지 명확하게 추적 가능

  • 테스트 용이
    MVI 패턴은 흐름이 명확하게 분리되어 있고 순수 함수 기반 설계가 가능하기 때문에
    테스트 작성이 매우 수월
    특히 사용자 액션 Intent(Event)가 주어졌을 때 
    어떤 State 가 반환되어야 하는지를 예측 가능하게 만들수 있어 단위 테스트 작성이 간단해짐

  • UI 일관성 유지
    View는 오직 State만 렌더링하고, 기존 LiveData의 옵저버를 사용하지 않기 때문에 View와 ViewModel의 역할을 더욱 명확히 구분할 수 있다.

  • 구조 변화는 최소
    기존 클린 아키텍처에서 Domain과 Presentation(ViewModel)이 잘 분리돼 있었다면, 큰 수정 없이 Compose와 MVI 도입이 가능하다.

단점

  • 초기 진입 난이도
    MVI 구조 자체가 익숙하지 않은 개발자에게는 개념을 이해하고 전체 흐름을 설계하는 데 시간이 필요.
    특히 기존 MVVM에 익숙한 팀이라면 패턴 전환에 적응 기간이 필요합니다.

  • 보일러플레이트 증가
    Intent(Event), State, enum class 등을 명시적으로 분리해 정의해야 하기 때문에
    초기 구성 시 코드와 파일 수가 많아지고, 설계에 더 많은 신경을 써야 합니다.

  • 학습비용
    팀원 모두가 Compose와 MVI 패턴의 흐름을 잘 이해하고 있어야 원활한 협업과 유지보수가 가능합니다.
    구조에 대한 이해가 부족할 경우 오히려 코드의 복잡도가 높아질 수 있습니다.

  • 복합 설계 필요
    실제 프로젝트에서는 Compose + MVI만으로는 구조적 시너지를 내기 어렵고,
    MVVM이나 클린 아키텍처와 함께 사용해야 진가를 발휘합니다.
    이로 인해 전체적인 러닝커브가 더욱 높아집니다.
  • 소규모 앱에는 과한 설계일 수 있음
    규모가 작거나 복잡하지 않은 앱에서는 굳이 MVI나 MVVM 같은 구조를 적용하지 않아도 무방합니다.
    오히려 불필요하게 구조가 복잡해질 수 있으므로, 대규모 앱의 구조 개선 시에만 신중하게 도입하는 것이 바람직합니다.

 

마무리하며

이번 구조 개편을 통해 Android 앱은

Activity View 중심 구조 에서 MVVM 구조로,

MVVM 구조에서 클린아키텍처 와 멀티모듈로,
XML 기반 UI에서 Compose 와 MVI 구조로 완전히 전환되었습니다.

이 변화는 단순히 기술 스택을 바꾼 것이 아니라,
팀 전체의 개발 문화와 문제를 바라보는 방식에도 영향을 준 전환점이었습니다.

비록 이번 글이 시리즈의 마지막이지만,
비슷한 고민을 하고 있는 Android 팀에게 조금이나마 방향성과 실마리를 제공할 수 있었다면 그걸로 충분히 의미가 있다고 생각합니다

 

물론 저에게도 아직까지는 선언형 UI Compose익숙함과 낯섦이 공존하는 기술입니다.
오랜 시간 동안 XML 기반 개발에 익숙해져 있던 만큼, 선언적 UI 방식은 여전히 적응 중이고,
Android Studio의 퍼포먼스나 렌더링 지연 같은 툴링 이슈는 때때로 실무 흐름을 끊어놓기도 합니다.

그럼에도 불구하고, Android든 iOS든 모바일 플랫폼은 끊임없이 변화하고 있으며,
이 변화에 적응하려면 결국 ‘계속 배우고, 적용해보고, 개선해 나가는 과정’을 받아들여야 한다고 믿습니다.

클린아키텍처 , 디자인패턴 , Jetpack Compose는 그 여정 속에 놓인 하나의 도구일 뿐입니다.
그리고 시간이 흐를수록, 우리는 이 도구를 점점 더 잘 다루게 될 것이고,
그만큼 더 나은 사용자 경험과 개발 효율성을 만들어낼 수 있을 거라 생각합니다.

 

이 글이 Android 프로젝트 구조 전환을 고민하고 있는 누군가에게 작은 용기와 방향이 되었기를 바랍니다.
긴 글 읽어주셔서 감사합니다.


감사합니다!

반응형