SwiftUI + MVVM의 대한 고찰

Flamozzi·2023년 1월 19일
2
post-thumbnail

SwiftUI를 이용하여 iOS 개발을 함에 있어서 어떤 프로젝트 패턴을 따를지에 대한 논의가 활발하게 일어나고 있으며, 그 중 MVVM 구조를 탈피하려는 움직임이 많이 보이기에, 이 기회에 관련 자료들과 주장들을 통해 생각을 정리해보기 위해 본 글을 작성합니다. 참고한 링크들의 원본 글들을 토대로 나의 생각을 추가로 녹여 정리해볼 기회로 이용하기 위해 작성합니다.

SwiftUI에서 MVVM 구조를 탈피하려는 움직임이 일어나는 배경

  • iOS 개발에서 선언형 뷰 프로그래밍 방식인 SwiftUI가 더욱 널리 쓰이기 시작하고 있다.
  • SwiftUI를 접한 개발자들은 기존의 ViewModel을 두고 데이터 바인딩을 해주는 역할을 수행하며 효율적인 프로젝트 구조로 알려진 MVVM을 SwiftUI에도 적용하며 프로젝트를 진행해왔다.
  • 하지만, 애플 디벨로퍼 포럼을 비롯한 여러 개발자들은 선언적 UI 환경에서 MVVM을 사용하는 것이 불필요하다 라는 주장을 한다.

SwiftUI에서 ViewModel이 필요하지 않은 이유는 무엇인가?

  • 그동안 아무 의심 없이 MVC보다 더 진화한 MVVM 구조가 더 좋은 것이라고 여기며 당연하게 사용해온 편협한 인식이 존재했다.
  • 실제로 나를 포함한 여러 개발자들은 MVVM 패턴을 지향해왔으며, MVVM은 비지니스 로직을 분리 시키는 목적으로도 쓰이기 때문에 여전히 좋은 구조임에 틀림 없다.
  • 하지만 실제로 SwiftUI로 개발을 하다보면 MVVM 구조를 지키기 위해 억지로 ViewModel을 만드는 상황이 종종 발생한다.
  • 여기서 고민해 볼 부분이 발생한다.
  • 비니지스 로직을 분리시키 위한 용도로 ViewModel을 만드는것이 과연 올바른가?
  • SwiftUI에서 View는 자체적으로 Data Binding이 가능한 PropertyWrapper를 지원한다.
import SwiftUI

struct ContentView: View {
  @State var person = Person()
  
  var body: some View {
    Text(person.name)
	  ...
  }
}
  • 위 코드를 보면 person이라는 model instance가 @State 변수로 선언 되어 있다.
  • 해당 모델 값이 비지니스 로직을 통해 바뀌면 person instance의 값도 바뀐다.
  • 그렇게 되면, 데이터 바인딩이 View에서 이미 되어 있기에, View에 나타나는 text의 name이 바뀌게 된다.
  • 즉, SwiftUI에서의 View는 이미 View + ViewModel의 역할을 하고 있다.
  • ViewModel이라는 것은 상태를 View에 Binding해서 Reactive로 반영하는 것이 목적으로 도입되었지만, 선언적 UI에 그 기능이 내포되어버렸기에 ViewModel은 불필요하다.

MVVM이 불필요하다는 여러 개발자들의 의견이 있다 (해외에서도 이미 종종 논란이 됨)

  • 애플 디벨로퍼 포럼을 포함한 다양한 아티클에서 개발자들은 SwiftUI에서 MVVM을 사용하는 것이 불필요하다고 주장하고 있다. (참고 링크: https://developer.apple.com/forums/thread/699003)
  • 위의 아티클에서는 SwiftUI를 쓰면서 MVVM을 사용하는 것은 “날 수 있는 보드에다가 바퀴를 굳이 달아 달리게끔 변경하는 것”과 같다고 말한다.
  • 이미 잘 날 수 있게 개발된 SwiftUI에 억지로 MVVM 구조를 맞추기 위해 바퀴를 달아 달릴 수 있게 개조하는 것도 나쁘지는 않지만, 굳이 불필요한 과정을 통해 오히려 코드가 많아지고 억지로 끼워맞춘 느낌처럼 보이게 된다고 주장한다.
  • 즉 SwiftUI에서는 사실 Model과 View가 결합된 MV면 충분할 수 있다. (Controller 혹은 ViewModel이 필요 없다)
  • 실제로 개발하던 Paperful 프로젝트 또한 MVVM을 매우 엄격하게 따르고 개발하려다보니, 삐걱거리는 부분이 분명하게 존재했다. (불필요한 뷰모델 생성, 양방향 데이터 플로우 구조, 코드의 과도한 길이 등)

Apple이 SwiftUI 섹션에서 권장하는 데이터 플로우 아키텍처

  • Apple 공식에서는 단방향 Data Flow를 권장 및 소개하고 있다.
  • State를 관리해줌으로 View를 나타낸다 = 상태 기반의 단방향 구조
  • 최상단에 보이는 User로부터 어떠한 사용자 interaction(주문 버튼이 눌린다던지 하는..)이 들어오면, Action을 감지하고 side effect(네트워크 통신, 시스템 설정 등등)을 처리하고 State(상태)를 변경해준다. 변경된 상태는 View와 바인딩되어 있기 때문애, View를 업데이트 해주고 이를 유저에게 Rendering하여 보여주는 구조이다. ⇒ 매우 단방향적이고 잘 나뉘어진 구조이다!
  • 위의 애플에서 권장 및 소개하고 있는 단방향 아키텍처를 기반으로 참고한 링크들의 필자들은 “SwiftUI에서 MVVM을 사용하지 말자!가 아닌 ViewModel이라는 역할이 필요가 없으니 상태를 효율적으로 관리하기 위해 어떤 데이터 플로우 구조가 효율적일지를 고민하자" 라는 주장을 하고 있다.
  • 하지만 원글의 필자들은 “ViewModel = Data Binding을 위함”에 초점을 둔 것으로 보인다.
  • 따라서 MVVM의 구조에서 채택하던 중간 레이어인 ViewModel에 굳이 데이터 바인딩을 위해 State값들을 넣어두고 뷰에서 다시 바인딩 시킬 필요는 없고, 비니지스 로직만 들어가 있으면 훨씬 쉽게 사용할 수 있을 것이라는 주장이 있다. (해당 네이밍을 ViewModel이 아닌 Store혹은 이외의 이름을 채용하려는 움직임인듯)

디자인 패턴을 위한 디자인 패턴?

  • SwiftUI에서 MVVM을 사용하는 것 ⇒ SwiftUI + MVVM ⇒ MVVM on MVVM과 같은 논리가 된다.
SwiftUI.View   <->   ViewModel  <->  Model
             Binding

MVVM           on    MVVM
  • 이중 MVVM 구조와 같은 괴이한 상태가 된다.
  • 안된다기 보단 불필요한 구조가 된다.
  • 애플 공식문서나 어디에도 MVVM 구조를 권장하는 내용이 없음에도 불구하고 개발자들이 오랜시간 자리잡은 아키텍처 패턴인 MVVM을 사용해야한다는 고정관념이 생겨버린 것으로 판단된다.
  • 실제로 Flutter를 포함한 다양한 선언형 UI를 사용하는 환경에서는 기본적으로 MVVM 패턴을 사용하고 있지 않다. (실제로 나는 iOS 개발에서 익숙한 MVVM 패턴을 활용하기 위해 사이드 프로젝트인 ‘미팅학개론’ 에서도 MVVM을 개선시켜 GetX를 붙인 새로운 구조를 팀 내에서 고안하고 사용중에 있다. 이 또한 관습에서 비롯된 불필요한 아키텍처 팩토링의 결과일 수도, 혹은 더 효율적이고 안전한 구조를 위한 시도였을지도 모르겠다.)

그렇다면 MVVM 대신 어떤 구조를 사용해야 하는가?

  • SwitfUI에서 MVVM을 지양한다고 했을 때 생기는 의문은 다음과 같다.
    • View에 Model 객체도 있고 이에 관련한 모든 비지니스 로직이 들어가는 MV구조는 옳은 것인가?
    • 로직과 UI의 분리는 꼭 필요하다고 생각하는데 이는 어떻게 해줘야 하는것인가?
    • ViewModel을 사용하지 않는다면 이 분리는 어디서 담당하는가?
  • 이 문제에 대해서 원글 필자는 두 가지 방법을 제안한다.
  • 첫번째, Model에서 이를 구현한다.
  • 두번째, Flux*개념의 Store로 분리한다.
    (* Flux란 페이스북에서 MVC 문제를 해결할 목적으로 고안한 아키텍처이다. 단방향 데이터 흐름에 유리한 구조를 띈다.)
  • 하지만 첫번째 방법은 모델에 비지니스 로직을 넣게 되고, 이는 다시 단방향 플로우를 해칠 수 있다.
  • 따라서 Flux적인 Stroe의 개념으로 분리하여 Store에서 View를 나타낼 상태 즉 State를 관리해주는 것이 적절하다.

선언형 UI를 위한 아키텍처는 무엇인가?

  • 무조건적으로 모바일 개발에서 MVVM 패턴을 사용하는 것은 이제는 지양해야 할 시점이라고 생각한다.
  • 실제로 SwiftUI를 통해 개발을 해본 사람이라면 SwiftUI 개발을 하는데에 가장 표준적인 아키텍처 혹은 Best practice라고 할만한 것이 아직 없다는 사실을 알고 있다.
  • 선언형 UI인 SwiftUI는 이미 MVVM 정도의 기능은 기본으로 가능하게 되었다.
  • 즉 선언형 UI를 사용하는 환경에서는 MVI(단방향 데이터 플로우), Flux가 더 잘 어울리는 패턴이라고 할 수 있다.
  • 개인적으로 리서치를 해 본 결과, TCA 패턴이 앞선 문제점들과 해결 방안을 포함하여 MVVM 패턴의 좋은 대안이 될 수 있다고 생각한다.

    **The Composable Architecture:** https://github.com/pointfreeco/swift-composable-architecture
    TCA 국문 번역본: https://gist.github.com/pilgwon/ea05e2207ab68bdd1f49dff97b293b17

결론

  • SwiftUI 아키텍처에 대해 가장 좋은 방법에 대해서는 여러 논란이 있고, 정형화된 정도(standard way)가 현재는 존재하지 않는다.
  • 기존 앱 개발에서 많이 쓰이던 MVVM 패턴을 SwiftUI를 이용한 iOS 개발에도 적용하여 사용하려는 움직임이 실제로 많았고, 현재 기업들에서도 MVVM 패턴을 적극 사용중이다.
  • 하지만 사용하면 안된다가 아닌, 필요가 없다라는 측면에서 ViewModel는 더이상 필요가 없다고 생각한다.
  • 정확히는 View에서 로직과 관련된 코드를 분리시키기 위한 장소로써 ViewModel이라는 단어를 사용하는 것에는 논란의 여지가 많다.
  • 따라서 MVI, Flux, TCA등의 단방향 데이터 플로우를 가지는 여러 구조들이 각광받고 있다.
  • 하지만 어플리케이션에 따라 필요한 아키텍처는 분명 다르고, 실제로 고민의 결과가 MVVM이라면 MVVM 구조도 충분히 좋은 아키텍처일 수 있다. 모든 것은 케이스에 따라 다르다.
  • 위의 원글 저자들도 모두 데이터 바인딩을 위한 뷰모델 사용을 지양하자는 것이다. 비지니스 로직을 분리하기 위해서 뷰모델 구조를 사용하는 것은 타당하다. 하지만 그 단어를 store 등으로 바꿈으로써, 모호함을 해결할 수 있을 거 같다.
  • 따라서 SwiftUI를 포함한 선언형 UI를 사용하는 앱 개발에서 당연히 MVVM을 사용한다는 아키텍처링을 지양하고, 상황에 따라 적절한 아키텍처 선택이 필요해보인다. (실제로 SwiftUI에 MVVM과 클린 아키텍처는 과하다는 의견도 종종 보인다.)
  • 하지만 반대로 SwiftUI는 이제 막 성장하고 있기 때문에 충분한 아키텍처에 대한 양질의 자료가 부족한 상황이다. 또한 실제 기업은 MVVM 패턴을 사용하고 있는 경우가 많기 때문에 각 서비스 및 어플리케이션에 맞게 “잘 고민해서” 아키텍처를 선택하는 것이 중요하다고 생각한다.
  • TCA를 이용한 사이드 프로젝트를 진행하며, SwiftUI에 가장 잘 어울리는 아키텍처를 고민해 볼 계획이다.
  • SwiftUI가 아닌 UIkit + MVVM 패턴은 실제 현업에서 가장 많이 쓰이고 있으며, 그에 대한 충분한 학습을 진행할 계획이다.

참고 자료

profile
개발도 하고, 글도 적고

0개의 댓글