특정 아키텍처를 프로젝트에 도입하면 시간이 좀 지나야 장단점을 알 수 있습니다. 그런데 그 시기가 되면 다른 패턴으로 뒤집어 엎기가 상당히 어렵습니다. 그래서 어떤 방법으로 앱을 구성해 나갈 것인지는 신중히 결정해야 합니다.

             V            C             M
          View  ──    Controller    ──  Model
  UiController  ── LogicController  ──  DataController
================================================================
          View  ──     ViewModel    ──  Model         : MVVM
  Presentation  ──     Business     ──  Persistence   : Layered
  Presentation  ──      Usecase     ──  Data          : Clean
    Controller  ──      Service     ──  Repository    : Spring
ViewController  ──     Delegate     ──  CoreData      : iOS
      Fragment  ──     Delegate     ──  Room          : Android

이름이야 어찌 붙이든, 각자 역할에 따른 격리만 제대로 이루어 진다면 소프트웨어를 구성하는 데 큰 문제는 없습니다. 다만 우리가 조금 더 유지보수를 쉽게 하기 위해 이 영역을 손질하게 되는 것이죠.

변하기 쉬운 것과 변하기 어려운 것

아마 아키텍처를 찾아보는 개발자들은 로버트 마틴Clean Architecture 를 한 번쯤 읽어봤을 겁니다. 정말 훌륭한 책입니다.

The Clean Architecture

이분은 시스템 설계 시 무얼 제일 중요하게 생각해야 하는지에 대해서 얘기합니다. 그림에서 보면 외부 동심원은 교체하기 쉽고, 내부 동심원은 보호됩니다. UIDB 따위는 곁가지라는 것입니다. 소프트웨어의 의존 방향이 시스템의 핵심 가치를 담고 있는 엔티티로 향해야 한다는 점을 강조합니다. 왜냐면 소프트웨어를 변경하기 쉽도록 유연하게 만들기 위해서입니다.

그렇습니다. 유연해야 유지보수하기 수월합니다. 형태가 단순하면 더 좋겠죠!

서비스와 상태 변화

서비스의 실행은 필연적으로 앱의 상태를 변화시킵니다. 그런데 호출한 객체뿐만 아니라 다른 객체에도 영향을 미치는 상태값이라면 이를 연결하는 중개자 객체가 하나 더 필요하게 됩니다. 앱은 더욱 복잡해지는 것이죠. 어떻게 이를 단순화 시키면서도 클린 아키텍처의 가치를 실현할 수가 있을까요?

몇 년 전에 이 문제에 대해 골머리를 앓다가 정말 마음에 드는 글을 발견했습니다.

상태 처리 문제를 Redux 와 비슷하게 single source of truth 로 해결했더군요. 정말 훌륭합니다. 큰 도움이 되었고 감동했습니다. 뷰 코디네이터에 대한 견해도 저와 완전히 일치했습니다. 저는 명령형 UI를 사용하던 시기에 라우터를 통해 뷰를 관리한 적이 있는데, 기대와는 달리 유지보수에 아무런 도움이 되지 않아 폐기한 적이 있습니다. 선언형 UI 에서는 더더욱 필요 없습니다. 한편, 인젝션과 인터랙터 처리 부분은 그리 마음에 들지 않았습니다. 조금 더 단순하면 좋겠습니다.

앞서 소개한 것과 비슷하지만 좀더 복잡한 TCA 라는 단방향 아키텍처도 있습니다. 라이브러리까지 깔아야 합니다. 흐음…

단순하게, 더 직관적으로

우리는 만들어야 할 게 많습니다. 아키텍처는 쿠킹호일보다 얇아야 합니다. 여기서 힘빼기 시작하면 우리가 앞으로 요리할 것들에 에너지를 집중할 수 없습니다. 아키텍처가 복잡하면 그림은 우아하게 그려질지 몰라도 유지보수에 아무런 도움에 되지 않습니다. 뭐 하나 수정하고 만들어 붙일 때마다 부담이 클 겁니다. 저는 아키텍처 패턴이 최대한 단순하고 직관적이어야 한다고 생각합니다. 로버트 마틴 선생이 동그라미 및 개 그려 설계 방향을 제시하는 걸 보면 감탄이 나옵니다.

Screen Service Worker

일반적인 MVVM 에서 ViewModel 은 뷰와 바인딩되며, 뷰에 필요한 데이터를 매핑하고, 액션을 처리하기 위해 비스니스 로직을 수행하게 됩니다. 이러한 까닭에 뷰모델은 쉽게 비대해집니다. 뿐만 아니라 여러 뷰모델에서 동일한 로직을 처리할 경우 코드가 중복되므로 변경사항을 적용하기 위해서는 해당 파트를 모두 찾아 수정해야 합니다.

이 문제를 해결하기 위해 뷰모델에서 비즈니스 로직을 빼 여러 개의 유스케이스로 나누겠습니다. 우리가 MVC 에서 델리게이트 했던 것처럼요. 이렇게 하면 여러 뷰모델에서도 일관되게 로직을 수행할 수 있어 유지보수에 큰 도움이 됩니다.

비즈니스 로직이 없는 뷰모델은 이제 서비스의 자격을 잃고 뷰 영역으로 밀려나게 됩니다. 뷰는 뷰모델을 가져도 되고 안 가져도 됩니다. 선택사항입니다. 뷰모델이 있다면 더 좋겠지만, 뷰 자체가 너무 간단한 경우 굳이 만들지 않아도 괜찮습니다.

또한 AppState 를 도입했습니다. AppState 는 뷰나 뷰모델이 가지고 가지고 있는 상태 프로퍼티를 변화시키는 트리거입니다. Read-Only 속성으로 할당하고, 수정하고 싶으면 반드시 유스케이스를 호출하도록 만들었습니다. AppState 가 업데이트되면 이를 감시하는 여러 객체가 반응할 겁니다.

유스케이스는 액션별로 나눠서 구성됩니다. 여러 데이터 컨트롤러를 넘나들며 비즈니스 로직을 처리하게 될 겁니다. 그 결과를 반환하거나 AppState 를 업데이트할 수도 있습니다. 이것은 유스케이스의 선택사항입니다.

오늘은 여기까지 하겠습니다. 다음 포스트에서는 실제 코틀린과 스위프트 코드를 통해 구성해 보겠습니다.