어려운 MVVM
MVC
MVC단점
MVVM
코드에 적용하기
MVC 패턴
🔆 model : data schema, server data model, 일반 데이터 모델 - 화면과 상관없는 데이터를 다루는 영역
🔆 view : label, button 등
🔆 controller: model이 가지고 있는 정보를 controller에서 다룸, 인스턴스 변수를 만들어서 데이터 활용
1. controller는 model한테 데이터를 달라고 말할 수 있음 = controller는 model에 항상 접근할 수 있다.
- controller에서 인스턴스를 통해서 model에 접근하는 과정을 볼 수 있움
2. view는 자기가 어떤 컨트롤러에 속해있는지 모른다.
- 어디에 속해있는지 알려주기 위해 outlet과 인스턴스를 통해 알려줌
- 애플이 정의해둔 IBAction/action을 통해서 controller와 소통이 가능
- view에서 가지고 있는 델리게이트와 데이터소스를 controller에서 구현하게 됨
3. model과 view는 서로 소통하지 않는다.
- controller를 통해 접근 가능
MVC의 단점
결국 model, view는 controller에 의존하게 되면서 controller가 비약적으로 커지게 됨, 명확한 분리가 애매함
가장 길게 작성되는 건 controller 파일이다. Massive View Controller~ezr~
model과 view가 서로 통신을 못하니까 생기는 문제인데, 이걸 해결할 수 있는 방법이 없을까? 라는 생각이 나오게 됨
예를 들어서 로그인 뷰가 있다고 생각하면,
- model에는 id/pw 데이터 모델
- VC(controller)에는 IBOutlet, 화면전환, 로그인 버튼 눌렀을 때 일어나는 다양한 id/pw 유효성 관련 조건 코드 등
여기서 VC은 view와 controller 둘 다의 역할을 하는 중
첫 번째로, controller에서 IBOutlet, 화면전환은 따로 떼기에는 어렵다.
그렇다면 두 번째로,
[ 사용자의 id가 UserDefaults에 저장이 되었나? ]
[ Date 양식을 어떻게 처리해주면 좋을까? ] 등의 다양한 로직/기능적인 처리가 있다.
이걸 비즈니스 로직이라고 한다. UI에 바로 표현하지 않고 가공하는 코드를 말하는데, 이걸 따로 빼보면 어떨까?라는 지점에서
ViewModel이 나왔다.
🔰 즉, ViewController가 담당하는 역할을 다른 친구에게 위임하면서 나온 것이 MVVM의 ViewModel
MVVM 패턴
🔆 model : 데이터가 들어있고, view(ui)와 상관이 없는 데이터를 다루는 독립적인 코드
- 본질적인 데이터라 "The Truth"라고 표현
- UIKit을 import하지 않는다.
🔆 view : label, button 같은 요소
🔆 viewModel : 중개자이자, 비즈니스 로직을 담당
괴물이라는 데이터가 있다면, view에 해당 데이터를 보여줘야 함
MVC에서는 ViewController를 통해서 구현을 했었는데,
MVVM에서는
(1) ViewModel이 들어오면서 model의 변경사항을 view에게 전달해주고,
(2) 사용자가 textField에 내용을 입력하면 model의 정보가 변경이 되면서 ViewModel에게 부탁을 해서 model에게 변경사항을 알려줌
한마디로, 중개자의 역할이다.
- ViewModel을 통해 Model의 데이터를 반영
- ViewModel에서는 데이터가 변경됐다는 것을 view에게 (publishing=)발표해준다.
- View에서는
(1) Model에 의해 (ViewModel을 거쳐) 데이터의 변경이 들어오기도 하고,
(2) 사용자에 의해 데이터의 변경이 들어오기도 해서
데이터 바인딩이 양방향으로 일어난다는 것이 특징
Data Binding, 데이터 바인딩
View에게 Model의 데이터가 변경된 것을 알려주기 위해 ViewModel에서 사용하는 방법이다.
가장 흔한 방식이 프로퍼티 옵저버 didSet을 사용하는 방법이라고 한다.
수업에서 써준 코드,,
1). Observable 클래스를 만든다.
2). Observable 클래스에는 listener, value 프로퍼티랑 bind 메소드가 있음
- listener : value 프로퍼티의 didSet 내에 구현되어서 value 값이 바뀔 때마다 listener가 호출되고, 클로저임
- value : 값이 변경되면 listener가 호출됨
데이터 바인딩을 적용해줄 프로퍼티를 (여기서는 Person 데이터의 변경을 알려줄 거니까 Person) Observable의 인스턴스로 만든다.
bind 함수를 통해서 listener를 전달한다.
으아 어렵다잉
흐름을 살펴보자...
0. PersonViewModel 인스턴스 생성
1. VC 실행 시에 viewDidLoad가 실행되면서 viewModel.fetchPerson(~)을 요청
근데 네트워크 통신이기 때문에 바로 응답이 오지 않아서, 응답이 올 때까지 기다리지 않고 다른 작업을 함. 비동기로 처리
2. 그 다음 bind 함수 호출해서 실행
Observable의 bind 함수가 호출돼서 콘솔창에 "옵저버블의 바인드함수"가 먼저 찍힘
3. bind 함수의 클로저 구문이 실행되고, (= closure(value))
VC에서 bind 함수의 클로저가 실행되어서 "뷰컨의 바인드구문"이 그 다음 찍히고,
label에 데이터 넣고, tableView 갱신을 실행해달라고 함
4. listener에게 업데이트하라는 의미로 listener = closure를 넣어둠
(원래는 옵셔널로 비어있는 코드였지만, tableView를 갱신하라는 코드블럭을 갖게 됨)
(그래서 value가 달라질 때마다, tableView.reload 할 수 있는 권한을 갖게 돼서 리로드가 가능해지는 것임)
5.
그 다음으로 dump(person)이 출력되는 것은 fetchPerson이 다 실행되었다는 것을 알 수 있음
list.value에 person을 넣어줬다는 건 value의 값이 변경됐다는 것
그렇다면, didSet 프로퍼티 옵저버를 통해서 tableView.reload가 가능해짐 => listener?(value) 로 넣어줫으니까
fetchPerson을 호출하는 당시에는 listener가 옵셔널이고,
bind 호출 시에야 listener에 값이 들어가게 됨
ViewModel 한 번 보면 모든 로직 처리를 여기서 해줌
만약 나중에 테스트를 해보겠다? 싶으면 public 제한자 달아주면 된다고 함..
결론적으로, model에 대한 정보는 viewModel에서 가져온다
Observable 클래스는 양방향 바인딩을 위해 구현한 것이고 이게 더 나아가면 RxSwift가 되는 것
양방향이 되면 반응형이다..라고 말해주는 것 그래서 Reactive Swift..?
학수고대하던 날이다. MVVM.. enum 자식을 적용해보려고 많은 시간 시도했으나.... 번번히 실패하던 지난날들
내 daegari.. yonggari인가... 여전히 어렵다...
요즘 왜 주변에 이렇게 의존성 관련된 이야기가 많이 나오는지..
view와 model이 너무 controller한테 의존해버리는 바람에 생기는 문제를 해결해주기 위해 나온 아이유.
덕분에 MVVM을 사용하면 ViewModel에는 UIKit 관련 코드가 없어서 테스트 하기에 용이하다구 한다.
비즈니스 로직을 쉽게 테스트해볼 수 있다니.. 사실 나에게는 테스트 코드.. 잘 모르겠지만,
'⭐️ 개발 > iOS & Swift' 카테고리의 다른 글
[iOS] Remote Notification (2) | 2022.10.11 |
---|---|
[iOS] 다국어 지원 i18n, l10n (1) | 2022.09.06 |
[iOS] URLSession(1) (0) | 2022.08.30 |
[Swift] Codable (2) | 2022.08.29 |
[Swift] Error Handling (4) | 2022.08.26 |