안드로이드

아키텍처는 왜 중요한가? (MVC, MVP, MVVM, Clean Architecture, ViewModel, 모듈) 알아 보자!

코딩하는후운 2024. 3. 26. 15:10
반응형

아키텍처는 왜 중요한가? (MVC, MVP, MVVM, Clean Architecture, ViewModel, 모듈) 알아 보자!

아키텍처는 왜 중요한가?

모바일의 특징을 고려

  • 네트워크적 특징을 고려
  • 사용자 이벤트
  • 다른앱들의 실행. 전화 문자 등등
  • 반응형 프로그래밍 (코루틴, RX Java 등)
  • 이런것들의 동시에 일어난다.
  • 빠른 배포가 필요

 

비지니스의 확장을 위해서

  • 모듈화가 필요하고 확장에 유연해야한다.
  • 실험적인 기능을 가설을 세우고 특정유저에게 보낸다 → 좋은 아키텍처가 뒷받침
  • Network Drivien UI → 비지니스 모델에 따라 지원해야할수도 있음

 

테스트를 위함

  • 확장성이 좋은 앱, 모듈간의 커플링이 낮음 → 결국 테스트하기 좋은 앱

 

아키텍처는 답이 없음 (중요!)

  • 앱에 실질적으로 도움이 되는것을 고민한다.
  • 정해진 정답이 있는 것이 아님
  • 각 아키텍처별로 몇가지 중요한 원칙에 기반해서 우리의 비지니스 모델에 맞게 구현하면 됨

→ 아키턱처를 이해해야 위와 같은 것들을 지원하기에 유리하다.

→ 즉 앱을 좀 더 효율적으로 만들고, 결과적으로 비지니스의 성공 위해 필요함

 

 

좋은 아키텍처와 복잡성

어떤설계가 좋은 설계인가?

  • 이해하기 쉬워야한다.
  • 버그가 발생했을때 쉽게 파악하고 쉽게 수정할 수 있어야한다.
  • 비지니스 요구사항에 맞게 쉽게 변경 가능해야한다.
  • 테스트하기 용이한 구조여야 한다.
  • 성능을 좋게 만들 수 있어야한다.

 

좋은 아키텍처의 방해물은 복잡성이다.

  • 복잡성이란 시스템을 이해하기 어렵고 수정하기 어렵게 만드는것.
  • 복잡하지만 않으면 어떤 구조던지 상관없음

 

왜 복잡해질까?

  • 강한 의존성은 복잡하게 만드는 원인이다. → 최소화 할 필요가 있음
  • 불명확성 확실하지 않은 것 → 기술부채

 

어떻게 복잡성을 낮출수 있을까?

  • 추상화
    • 알필요가 없는 불필요한 정보를 감춘다.
    • 깊은 모듈, 범용 인터페이스
  • 캡슐화

 

깊은 모듈

  • 사용자는 인터페이스만 알면 된다.
  • 실제 작업은 깊은 모듈에 걸쳐서 일어나지만 공개되지는 않는다.
  • 잘 나눠서 깊게 만든다.

얕은 모듈

  • 반면에 얕은 모듈은 여러가지 모듈에 걸쳐서 사용자가 계속해서 호출해야함
  • 사용자가 많은 것들을 알아야할 필요가 있음
  • 결국 의존성 결합도가 강해질 수밖에 없다.

범용 인터페이스

  • 범용으로 만들면 지금 비용은 들겠지만 추후 비용은 낮다.
  • 특정으로 만들면 지금은 금방해도 추후 비용은 높다.
  • 이 메소드가 얼마나 많은 상황에서 사용될수 있는가? → 많다면 범용.

정보 은닉

  • 일반적인 케이스를 최대한 심플하게 만든다.
  • 불필요한 정보를 감춘다. 퍼블릭, 프라이빗 을 잘 구분해서 사용한다.
  • 정보누출 → 같은 정보가 여러곳에서 사용되고 있다.

 

 

 

 

개방 폐쇄 원칙

  • 확장에는 열려있고 변경에는 닫혀있다.
  • 데이터구조에 필드를 추가하거나, 함수를 추가한다거나 하는 것에 열려있어야함.
  • 멤버변수를 변경한다고 해서 다른 사용자가 영향을 받아서는 안된다.
  • 내부코드를 변경한다고 해서 외부(사용하는 측면) 에서 영향을 받아서는 안된다.

 

글로벌 변수에 의존

  • 많은 클래스에 영향을 받게됨
  • 변경에 대해서 닫혀있는가? 아니다.

Abstract, Interface

  • 사용자에게 필요한 것만 노출해서 폐쇄원칙을 만족시키고
  • 구현클래스의 내용도 감춤으로서 수정도 용이해진다.

모뷸단위 개방 폐쇄 원칙

  • 의존관계 방향이 보호하려는 컴포넌트를 향하도록 그려진다.
  • 왜 RepositoryImpl 을 만드는가? → 의존성 역전원칙과도 관련있음

  • 위와 같이 구성하지 않았다면, 유즈케이스가 데이터소스까지 의존성을 계속해서 물고가게됨

 

MVx

  • 어떤 경우이든 모델은 분리된다.
  • 뷰의 역할을 할수 있는한 최대한 분리시켜야한다는것은 모두가 알고 있음

 

MVC

  • 개념자체는 위와 같음
  • Android 뿐 아니라 여러 플랫폼에서 적용되는 모델
  • 그러나 Android 에서는 View 와 Controller 의 분리가 쉽지 않다.
  • 결국 Activity 또는 Fragment 가 비대해지는 문제가 생긴다. → 관심사의 분리를 지키지 않았다.

  • 위 그림처럼 구성하면 문제를 해결할 수 있다.
  • 그러나 뷰 컨트롤러를 어떻게 액티비티의 라이프사이클과 연결할 것인가의 문제가 남는다.

 

 

MVP

  • IBM 에서 최초 구현, 마틴파울러의 소개로 알려짐
  • 안드로이드 초창기부터 전해져온 아키텍처
  • 뷰는 비지니스 로직에 관련된 부분을 관여하지 못하도록 분리한다.

  • 모든 UI 관련된 비지니스 로직을 프리젠터에서 처리
  • 뷰는 프리젠터의 요청에 따라서만 수동적으로 화면의 변화를 처리하게 된다.
  • 개방폐쇄의 원칙. 뷰보다 상위 수준인 프리젠터를 뷰의 변경으로부터 보호한다.
  • 보호할 수 있는 것은 아래와 같은 컨트랙트 인터페이스 패턴을 이용한다.

  • Contract 에 정의한 인터페이스를 통해서 뷰와 프리젠터간의 직접적인 연결을 끊는다.
  • 너무 상세한 로직은 뷰가 알고 있는것을 극복하기 위해 UIStateHolder 를 만들어 사용할 수 도 있다.

장점

  • 액티비티가 거대해지는것을 막는다.
  • 테스트 가능성이 증대된다. 프리젠터는 JVM 내에서 테스트가 가능해짐
  • 구조가 직관적이고 컨트랙트만 봐도 금방 구조를 알수 있음
  • 반응형 라이브러리를 쓰지 않아도 된다.

단점

  • 뷰의 기능이 명령형일때 잘 동작한다.
  • 기본적으로 뷰가 프리젠터를 참조하고 있다.
  • JetPack Compose 와 같이 선언형뷰에는 적용하기 어렵다.

(참고) 선언형과 명령형

  • 명령형은 어떤화면을 보여줘, 어떤 이벤트일떄 어떤 로직을 실행해
  • 선언형은 어떤 상태일때 어떤 것을 보여줄지 선언만 해둠
  • 그럼 상태가 변경되면 알아서 그상태에 맞는 뷰가 보여진다.

 

MVVM

  • MS 에서 처음 구현함
  • 마틴파울러의 프리젠테이션 도메인분리가 시초 (PDS)
  • 프리젠터와의 차이점은 뷰모델이 뷰를 제어하지 않는다는점
  • 뷰는 뷰모델을 호출하지만 결과는 콜백, 옵저버블형태로 받는다. RX

단방향 데이터 흐름

  • 상위객체는 하위객체로부터 상태를 읽을수 없다.
  • 뷰는 사용자 입력을 전달만 할뿐 직접적으로 결과를 받지 않음
  • 이벤트 형태로 상태 변경을 통보받아 화면을 업데이트한다.
  • 뷰모델은 어떤뷰가 이것을 받는지 모른다. 의존하지 않는다.

 

데이터 바인딩

  • 이벤트를 뷰로 바로 적용할수 있는 메커니즘을 제공한다.
  • 각종 작은 이벤트에 대한 뷰모델 처리 옵저버의 코드양을 줄일 수 있다.

 

안드로이드 아키텍처 가이드

https://developer.android.com/topic/architecture?hl=ko 

 

앱 아키텍처 가이드  |  Android 개발자  |  Android Developers

앱 아키텍처 가이드 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이 가이드에는 고품질의 강력한 앱을 빌드하기 위한 권장사항 및 권장 아키텍처가 포함

developer.android.com

  • 안드로이드에서는 권장하는 아키텍처 가이드를 계속해서 업데이트해주고 있음
  • 여기서 StateHolders 는 ViewModel 이 될 수 있다.
  • 따라서 이 모델은 MVVM 모델에 가깝다고 볼 수 있다. (Google 에서 MVVM 을 언급하지는 않음)

단점

  • 반응형에 대한 처리가 필요하다. RX 자바, 코루틴 등
  • 뷰모델이 커지는 것을 막을 수 없다.
  • 순환적인 이벤트 흐름때문에 발생하는 보일러플레이트 코드들

장점

  • 기존의 Activity, Fragment 구조를 유지하면서
  • 재사용성과 생산성 높은 아키텍처를 구현할 수 있다.
  • 안드로이드의 생존주기 처리에 가장 잘 어울린다.

 

Viewmodel in AAC

  • 생명주기 내에서 설정변경, 프로세스 종료가 일어나도 뷰모델의 내용이 보존되는 구조를 제공한다.
  • Flow, LiveData, Compose State 가 보존될수 있도록 하는 SavedStateHandle 을 제공한다.

Pluu Dev - SavedStateHandle을 다뤄봅니다

 

Pluu Dev - SavedStateHandle을 다뤄봅니다

[메모] Gradle의 Version Category를 사용하여 Extra Properties 호환성 유지 Posted on 25 Sep 2022 [메모] 앱의 Version Code 변경 Posted on 17 Sep 2022 [삽질] 버튼의 클릭 터치 영역 커스텀 해보기 Posted on 04 Sep 2022 Lint 작

pluu.github.io

  • Coroutines 가 뷰모델의 생명주기 내에서 동작할수 있는 환경을 지원한다.
    • androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0
class MyViewModel: ViewModel() {
    init {
        viewModelScope.launch {
            // Coroutine that will be canceled when the ViewModel is cleared.
        }
    }
}

 

ViewModel 사이의 상태공유

  • 로딩같은것을 화면전체에서 처리한다고 하면 액티비티 뷰모델에서 처리할수도 있음

 

어떻게 잘 구현할 것인가?

  • 뷰, 뷰모델을 최대한 분리한다.
  • 뷰모델에서 화면로직이 아닌 도메인 로직에 해당되는 부분을 도메인 계층으로 옮긴다.
  • 아래코드에서 잘못 구현한것은?

 

ViewModel 생성자에서 초기화 이상의 일을 해도될까?

  • 리스트를 로드한다던가 하는 등의 일을 해도 되나 NO
  • 단일책임원칙 위반 init 은 init 일뿐
  • 생성자에서 하는것은 좋지 않은 일
  • 리스트를 로드할지 안할지는 뷰의 역할

 

Clean Architecture

모바일 클린 아키텍처 이론은 사실 존재하지 않는 개념

  • 클린 아키텍처를 모바일에서 최대한 구현하기 위해서 노력해본 합의의 결과물
  • 엉클 밥의 클린아키텍처를 그대로 모바일로 가져갈수는 없음

 

선을 긋는 기술

  • 소프트웨어 아키텍처는 선을 긋는 기술
  • 이러한 선을 경계라고 부르며,
  • 경계로서 요소를 서로 분리하고, 반대편에 있는 요소를 알지 못하도록 막는다.
  • 경계선의 구분 → 단일 책임 원칙

 

도메인 계층

  • 유즈케이스
  • 가장 높은 수준의 계층 (과녁의 가운데)
  • 비지니스의 요구사항을 구현하고 있음

 

모바일에서의 그림

  • View
    • 직접적으로 플랫폼 의존적인 구현
    • 사용자 이벤트 UI, Context 등
  • Presenter
    • OS 렌더링 API 에 의존하지 않음
    • 뷰 관점에서의 비지니스 로직을 담당
  • UseCase
    • 도메인 계층의 비지니스 로직
    • 기획자 관점에서 구현이 가능한 로직
  • Model
    • 도메인 모델
  • Translator
    • 데이터계층의 엔티티와 도메인의 모델을 변환하는 역할. Mapper
  • Repository
    • 유즈케이스가 필요로 하는 데이터 처리에 대한 기능을 제공
    • 데이터소스를 인터페이스로 참조하기 떄문에 여기서 remote, local, mock 을 전환할 수 있음
  • Entity
    • 데이터 소스에서 사용되는 데이터를 정의한 모델

 

3가지 레이어에 속하지 않는 것들

  • App, Common 레이어를 만들어서 관리
  • Service, Hilt DI, 앱클래스, 등을 관리

 

 

참조 : 스터디 그룹

반응형