-
SwiftUI에서 MVVM 사용을 멈춰야 하는가?SwiftUI 2022. 7. 28. 11:12
안녕하세요. 그린입니다🟢
이번 포스팅에서는 요즘 아니 예전부터 조금 말이 많이 나오고 있던 SwiftUI를 쓰면서 MVVM 아키텍쳐 사용을 지양하는 의견들이 많이 나오고 있습니다.
이에 한 개발자가 생각을 정리한 레퍼런스를 보면서 제 의견도 같이 한번 정리해볼까합니다🙌
우선 많은 레퍼런스를 이것을 위해 봤는데요.
그중에서 지금부터 이 포스팅에 정리할 글들의 원본은 아래에서 참고했다고 보시면 됩니다!
물론 저기서 제 의견과 다른 부분도 있고 조금 더 정리해볼 부분도 있어서 이 부분에는 제 의견을 가미했습니다~
https://qiita.com/karamage/items/8a9c76caff187d3eb838
일본어라 너무 생소하고 어려울거라 생각해요.
그렇기에 저도 번역이 되어있는걸 찾아보았고 그 끝에 김은영 iOS 개발자님의 아주 상세하고 친절한 번역본을 많이 참고했습니다👍
https://gist.github.com/unnnyong/439555659aa04bbbf78b2fcae9de7661
그럼 한번 들어가보죠!
아 참고로 이 포스팅은 번역을 한것이 아닌 원작자의 글에서 큰 주제들에 대해 자의적인 저의 생각을 담았습니다.
SwiftUI에서 MVVM 사용을 멈추자라는 의견을 제시한 배경
요즘에는 iOS 개발에 있어 View Drawing 방식으로 쉽고 편리한 SwiftUI를 사용하고 있습니다.
SwiftUI는 선언형 뷰 프로그래밍 방식입니다.
즉 선언적 UI 환경에서 MVVM을 사용하는것이 적절한지에 대해 고민해보면 좋을것 같습니다.
MVVM이라하면 우리가 흔히 UIKit 환경에서 개발을 할때 ViewModel을 두고 데이터 바인딩을 해주는 역할을 해주게 됩니다.
즉 여기서 핵심은 ViewModel인데요.
과연 SwiftUI같은 선언적 UI 환경에서도 ViewModel이 필요할까요?
이 부분에서 의문이 들었고 우선 결론은 필요하진 않다로 나왔습니다.
그러면 왜 필요하지 않은지 또 필요하지 않다면 어떤식으로 구현을 하는것을 제시할 수 있는지 살펴보겠습니다.
SwiftUI에서 ViewModel이 필요하지 않은 이유
우리는 MVVM이라는것을 크게 고민안하고 앱 개발을 한다면 당연스럽게 사용하고 있었습니다.
아무 의심도 없이 그저 MVC보다 진보되었다는 생각과 MVVM의 구조가 좋은것! 이런 편협한 인식이 어느정도 존재했어요.
물론 여기서 취지는 "MVVM이 안좋다 이제 버려야한다" 라는 뜻은 절대 아니며 저또한 MVVM의 방식을 지향해왔습니다.
그런데 SwiftUI로 개발을 함에 있어 MVVM의 구조를 따라가려고 억지로 ViewModel을 만드는 그런 상황들이 많이 발생하고 있었어요.
이런 상황이 과연 의도한걸까요?
물론 ViewModel을 만든다고 나쁜것은 아닙니다.
비지니스 로직을 분리 시키는 목적으로도 사용할 수 있기때문이죠.
그렇다면 여기서 또 생각해볼것이 비지니스 로직을 분리시키기 위한 용도로 ViewModel을 만드는것이 의도한것이며 올바른가 일거같아요.
SwiftUI에서 View는 자체적으로 Data Binding이 가능한 PropertyWrapper를 지원해줍니다.
즉 아래 코드를 한번 보시죠.
import SwiftUI struct ContentView: View { @State var person = Person() var body: some View { Text(person.name) ... } }
위 코드에서 person이라는 모델 인스턴스가 @State 변수로 선언되어 있습니다.
즉 해당 모델 값이 비지니스 로직을 통해 바뀌면 저 person 인스턴스의 값도 바뀌는것이죠.
그럼 데이터 바인딩에 뷰에서 이미 되어있기에 뷰에 나타나는 text의 name이 바뀌게되죠.
즉 SwiftUI에서의 View는 이미 View + ViewModel인것입니다.
기존 UIKit 환경에서 MVVM의 구조가 도움이 되었던 이유
UIKit 환경에서는 View와 ViewModel의 데이터 바인딩을 Observable을 만들던가 아니면 React 구현체인 RxSwift 혹은 Combine을 통해서 리액티브하게 뷰에 값을 반영시켜주고 있었어요.
간단히 아래 Rx를 사용한 코드를 예시로 보겠습니다.
class MainViewController { @IBOutlet var nameLabel: UILabel! var viewModel = ViewModel() func setName() { viewModel.person.name .bind(to: nameLabel.rx.text) .disposed(by: DisposeBag() } ... } struct ViewModel { var person = Person() func changeName(text: String) { person.name = text } ... }
자 이런 코드가 있습니다.
UIKit 환경이고 nameLabel이라는 레이블이 뷰에 있을것이고 또 VC에 연결되어있어요.
ViewModel이라는 알고있는 뷰모델을 정의하고 이 타입을 VC에서 인스턴스로 생성해줍니다.
그럼 여기서 필요한건 person의 값이 바뀌었을때 nameLabel 즉 뷰에 나타날 text값을 변경해줘야합니다.
그러기 위해 우리는 이걸 직접 해줄수가 없기에 rx를 통해 데이터바인딩을 해주는 저런 코드를 사용하게 됩니다.
저게 우리가 흔히 알고 있는 ViewModel을 통해 뷰와 데이터 바인딩을 해주는 MVVM의 구조일거에요.
이러한 과정에서 ViewModel의 역할중 가장 중요한건 데이터 바인딩일거에요.
즉 모델과 뷰 사이에 양방향 소통을 해줌으로 바인딩을 시켜주는것이죠.
그런데 SwiftUI에서는 View에서 다 해줄수 있기에 이럴 필요가 없다는 소리입니다🙌
MVVM이 불필요하다는 여러 개발자들의 의견
애플 디벨로퍼 포럼, 유투브 및 그외 다양한 아티클에서도 SwiftUI에서 MVVM 사용하는것이 불필요하다고 주장하고 있습니다.
그 대표적인 아티클로는 아래에 예시가 아주 잘 정리되어 있다고 생각합니다.
https://developer.apple.com/forums/thread/699003
여기서 SwiftUI를 쓰면서 MVVM을 사용하는것은 "날 수 있는 보드에다가 바퀴를 굳이 달아 달리게끔 변경하는것"과 같다고 말하고 있어요.
이해가 잘 되시나요?
보시면 잘 날수 있게 개발된 SwiftUI에 억지로 MVVM의 구조를 맞추려다보니 바퀴를 달아 뭐 달리는것도 나쁘지 않지만 불필요한것을 통해 오히려 코드가 많아지고 MVVM에 억지로 맞춰끼워놓은 느낌처럼 보이고 해석됩니다.
즉 SwiftUI에서는 사실 모델과 뷰가 결합된 MV면 충분할 수 있습니다.
컨트롤러나 뷰모델이 필요없을 수 있죠.
SwiftUI에서 그러면 View에서 모든걸 다 해야하는가?
이는 또 아니라고 생각합니다.
선언형 UI에서 아까도 말했듯 ViewModel은 데이터바인딩이 주요 기능인데 이게 필요없어졌어요.
그렇기에 ViewModel이라고 표현하는것이 어색하다는 주장들입니다.
즉 SwiftUI에서 MVVM의 구조를 도입하는것이 아까도 말했듯 중간 계층의 레이어를 하나 더 두게 되는 복잡성을 야기시킵니다.
View와 Model 사이에 없어도되는 ViewModel을 끼워넣어버림으로 양방향 데이터 플로우 구조가 됩니다.
현재 가장 핫하고 장점이 많아 많이들 따르고 있는 단방향 아키텍쳐의 구조와는 멀다고 생각해요.
애플이 SwiftUI의 섹션에서 State와 Data Flow를 설명하면서도 보여준 예시가 있는데 이 또한 단방향 데이터 플로우를 권장하고 소개합니다.
보시면 상태 즉 State를 관리해줌으로 View를 나타내주면서 모든 흐름이 단방향 구조입니다.
즉 상태 관리 기반의 단방향 구조라고 생각할 수 있습니다.
최상단에 보이는 User로부터 어떠한 사용자 인터렉션 (주문 버튼이 눌린다던지)이 들어오면 이를 Action으로 감지하고 사이드 이펙트(네트워크 통신, 시스템 설정 등등)을 처리하고 State 즉 상태를 변경해줍니다.
변경된 상태는 View와 바인딩되어 있어 View를 업데이트 해주고 이를 유저에게 렌더링 시켜 보여주는 구조이죠.
아주 단방향스럽고 잘 나눠져있죠.
그렇기에 공식문서에서도 말하지만 선언형 UI를 사용하는 환경에서는 단방향의 데이터 플로우의 구조를 지향합니다.
원본의 필자는 Single Source of Truth를 지키기 위해 상태 관리를 하는 역할을 따로 만들어 관리하거나 Property Wrapper를 통해 어떻게 이 단방향 데이터 플로우를 효과적으로 사용할 수 있을지를 고민할 필요가 있다고합니다.
즉 정리하자면 조금 "아 다르고 어 다르다"의 느낌일 수도 있는데요.
"SwiftUI에서 MVVM을 사용하지 말자!가 아닌 ViewModel이라는 역할이 필요가 없으니 상태를 효율적으로 관리하기 위해 어떤 데이터 플로우 구조가 효율적일지를 고민하자"라고 생각해볼 수 있습니다.
그런데 필자는 뷰모델의 역할을 조금 단정지은것 같아요.
필자가 보는 뷰모델은 데이터 바인딩에 초점을 맞춘것이라 볼 수 있을것 같습니다.
비지니스 로직을 뷰모델에 담으면서 또 데이터 바인딩을 위해 한번 더 거치는 그런 상태를 염려했다고 생각해요.
그렇기에 제가 생각할때는 뷰모델이라는것을 만들어도 되지만 굳이 데이터 바인딩을 위해서 State값들을 넣어두고 뷰에서 다시 바인딩 시킬 필요는 없고 비지니스 로직만 들어가있으면 좋을거라 생각합니다.
그랬을때 이 네이밍을 뷰모델로 하지 않고 스토어 같은 네이밍을 채용하는것도 좋아보입니다.
즉 필자가 생각하는 MVVM의 ViewModel은 데이터 바인딩만을 말하는 경향이 보입니다.
MVVM이 왜 당연시 되게 된걸까?
저도 잘 모르겠습니다.
많은 개발자들이 아키텍쳐 패턴으로 MVVM을 사용하고 있습니다.
물론 당연히 잘못된건 아닙니다.
그런데 아까도 말했듯 SwiftUI에서 MVVM을 사용하는건 MVVM + MVVM 즉 이중으로 구현했다고 생각됩니다.
즉 계속 강조되지만 쓰면 안된다가 아니고 불필요하다라는 겁니다.
애플 공식문서나 어디에서도 MVVM에 대해 밀어주는것은 볼 수 없습니다.
어쩌면 개발자들에게 오랜시간 자리잡은 아키텍쳐 패턴이기에 고정관념이 생겨버린건 아닐지도 모르겠습니다.
요즘 웹에서의 React나 Vue 혹은 Flutter 개발에서도 MVVM이 사용되고 있지 않습니다.
공통점으로 친다면 선언형 UI를 사용하고 있는 환경에서는 MVVM 패턴을 사용하고 있지 않는다로 생각해볼 수 있을것 같네요.
물론 사용되고 있지 않는다는것이 모든 사람이 사용하고 있지 않다라는건 아니고 대부분 사용할 필요가 없어 사용하지 않는다라고 생각됩니다.
즉 ViewModel이 정말 어떤 환경에서도 우리들이 원하는 구조인지 생각해볼 필요가 있습니다.
MVVM 대신 그럼 뭐?
SwiftUI에서 MVVM을 지양하자는 소리는 계속 나열했으니 이해를 했다고 가정해봅시다.
그러면 하나 의문점이 들거에요.
UI 즉 View에 모델 객체도 있고 이에 관련한 모든 비지니스 로직이 들어가는 MV 구조가 옳은거냐?
로직과 UI의 분리는 꼭 필요하다고 생각하는데 이는 어떻게 해줘야하는거냐?
ViewModel을 사용하지 않는다면 이 분리는 어디서 담당해줘야될까?
이런 꼬리를 무는 의문점들이 생길거고 저도 그랬습니다.
물론 정말 토이 프로젝트 정도 규모거나 단순한 비지니스 로직을 가졌다면 이를 굳이 나누는것이 크게는 무의미하고 코스트가 더 들수도 있습니다.
그러나 그렇다 하더라도 우리는 더 구조적인 코드를 위해 나눠야하지 않을까요? (적어도 저는 그렇게 생각합니다!)
이에 대해서는 원본의 필자는 두가지를 말합니다.
첫번째는 Model에서 이를 구현해준다.
두번째는 Flux 개념의 Store로 분리해준다.
이 두가지 모두 가능합니다.
다만 저의 생각은 전자도 가능하지만 무의미하다고 생각하는측입니다.
모델에 비지니스 로직들을 넣게 되면 우리가 원하던 단방향 데이터 플로우를 또 해치게 된다고 봅니다.
그렇기에 Flux적인 Store의 개념으로 분리해 거기서 View를 나타낼 상태 즉 State를 관리해주는것이 적절하지 않을까 생각합니다.
그럼 우리는 어떤 구조를 사용하면 좋을까요?
선언형 UI에서의 구조
이제 "MVVM이 무조건적이지" 라는 시대는 끝났다고 생각합니다.
SwiftUI를 사용한다면 더더욱 그렇겠죠.
즉 선언형UI를 사용하는 환경에서 잘 어울린다고 생각하는것은 MVI, Flux정도라고 생각합니다.
MVI는 단방향 데이터 플로우 아키텍처입니다.
Flux는 Composit한 조합을 통해 상태 관리를 하는 마찬가지로 단방향의 아키텍쳐입니다.
즉 우리가 현재 필요한것들은 아래 사항과 같을거에요.
- View와 비지니스 로직 분리
- 단방향의 데이터 플로우
- 각 View를 통해 나타낼 Component의 설계 및 조합
- Component들의 상태 관리 및 연결
MVVM 아키텍처는 이런것들을 해결할 수 없다고 생각합니다.
그렇기에 웹에서의 Redux 기반이 앱에서도 필요합니다.
저는 이에 대한 아키텍처로 TCA(The Composable Architecture)가 적절하다고 생각합니다.
원본의 필자는 TCA에 대해 아직 시기상조이며 과장될 수 있다는 우려의 생각을 하고 있지만 저는 다른 의견입니다.
다만 필자의 말의 요지는 이해를 합니다.
과장의 우려가 될 수 있다는점은 앞서 말했듯 너무 작은 단위의 프로젝트일 때 불필요한 Core 파일 내 State, Action, Reducer를 만들거나 하는 작업들이 꼭 선행으로 이뤄져야하기에 배보다 배꼽이 더 클수도 있음에 우려하는거라 생각됩니다.
그렇다 하더라도 그런것들이 효율적인 관리와 추후 유지보수와 확장성을 위해 도움이 되는것이라는 전제하에서는 그런 구조를 지키는것이 상태 관리 기반의 단방향 아키텍처에 부합하지 않을까요?
물론 TCA가 짱짱 무조건 써야함 이런건 당연히 아닙니다.
각자의 개발환경과 추구하는 바에 의해 좋은 여러 선택지들중 하나로 TCA가 있다는 의견입니다.
필자가 어느정도로 TCA를 사용해봤는지는 파악할 수 없지만 TCA를 어느정도 빡세게 써본다면 시기상조라는 결론이 나오기는 힘들것 같습니다.
마무리
장황하게 위에서 많은 말이 오갔네요!
그렇기에 최종적으로 결론을 단순하게 지어보면 SwiftUI 환경에서 필요한건 MVVM 구조의 사용이 아닌 상태를 관리해주는것이 필요합니다.
그렇기에 상태 관리 기반의 단방향 아키텍처를 사용하는것이 좋은 대안이 될 수 있습니다.
다시 한번 또 말하지만 MVVM이 안좋아서 SwiftUI에서 무조건 사용하면 안돼는 절대 아닙니다.
각자 앱 개발 환경에 맞게 구현하는것이 가장 좋게 그게 MVVM 구조여도 잘못된것도 아니고 오히려 더 도움이 될 수 있습니다.
필자가 말했던건 뷰모델의 데이터 바인딩 부분만을 말하는 단편적인 시각이 있고 비지니스 로직까지 고려한 뷰모델을 생각한걸까라는 의문이 생깁니다.만약 비지니스 로직을 담기에 뷰모델을 만드는것도 일리가 있습니다.그럴때 뷰모델에 비지니스 로직만 담고 프로퍼티를 만들어 이를 다시 뷰에 바인딩 하는것으로는 뷰모델을 쓰지 말자입니다.
즉 계속 강조하고 강조되지만 데이터 바인딩을 위한 뷰모델을 지양하자라는 결론을 도출할 수 있을것 같습니다.
당연히 비지니스 로직을 분리하기에 뷰모델의 구조를 사용할 수 있습니다.
결국..! 항상 느끼지만 모든건 case by case 🤪
[참고자료]
https://qiita.com/karamage/items/8a9c76caff187d3eb838
https://azamsharp.com/2022/07/17/2022-swiftui-and-mvvm.html
https://developer.apple.com/forums/thread/699003
https://github.com/pointfreeco/swift-composable-architecture
'SwiftUI' 카테고리의 다른 글
@FocusState 사용하기 (2) 2022.09.05 SwiftUI - isDetailLink (With. NavigationLink) (2) 2022.08.23 SwiftUI 4.0 - NavigationPath (0) 2022.06.23 SwiftUI 4.0 - NavigationStack (0) 2022.06.20 SwiftUI 4.0 - Charts (0) 2022.06.10