 Apple Lover Developer & Artist

영속적인 디자인에 현대의 공감을 채워넣는 공방입니다

 Apple/Stanford iOS Programming (SwiftUI)

Lecture 3 Review Part 1: MVVM and the Swift type system

singularis7 2021. 8. 13. 00:54
반응형

MVVM Design Paradigm

MVVM (Model View ViewModel) 은 Design Paradigm 중 하나이며 SwiftUI 를 사용하기 위해 반드시 MVVM 설계 방식을 적용해야 한다.

*Design paradigm *
Makes it clear where all the diffrent components of your code go and how they interact with each other.

이전 방식으로 앱을 개발할 때 사용되는 UIKit 은 MVC (Model View Controller) 를 사용하며 MVVM 과는 다른 종류의 개발 방식이기 때문에 혼동하면 안된다.

MVVM 과 MVC 는 User Interface Code (View) 와 Backend Logic (Model) 을 분리한다는 공통적인 특징을 갖고있다. MVVM 에서 Model 과 View 가 어떤 mechanism 으로 동작하는지 살펴보자.


Model

1) The Model is completely UI independent

  • Model Swift Code 는 Swift UI 를 import 하지 않는다.

2) The Model captures all the data and logic that describes"what"your application does.

  • Card Game 을 구현할 때 모든 Card 들은 data 이며 카드를 선택했을 때와 같은 의사결정은 logic 이다.
  • data 와 logic 은 전부 Model에 구현되며 Model에만 존재해야한다.

이전에 작성한 Memorize Demo App에서 @State를 사용하여 UI에 logic 을 포함시킨 것은 MVVM Rule을 위반한 코드였기 때문에 MVVM 방식 따르도록 개선하려면 data 혹은 logic을 오직 Model을 통해 받아오도록 개선해야 한다.

View

1) The View is "how" your app is presented to your user.

2) The View is always going to be a reflection of the current state of the Model at all times

  • 앞면, 뒷면과 같은 Card의 상태 변동을 어떻게 View 와 연동시킬지에 관하여 위의 사유로 걱정하지 않아도 된다.
  • 화면에 보이는 모든 카드들은 Model에 있는 카드와 동일한 것이다.
  • 따라서 View 는 'Stateless' 하다.
Question: @State 저장되는 정보는 무엇인가?
Answer: how the view is managing itself.

@State 변수는 카드나 점수 또는 게임에 대한 정보를 갖지 않으며 UI 상의 일시적인 변화를 가능하게 만드는 용도로 사용된다. 예를 들어 현재 어떤 테마가 선택되었는지 알아야 할 때 사용된다. 이 때에도 게임에 관련된 모든 로직은 반드시 Model 에만 존재해야 한다.

3) What a view looks like is solely determined by what's in the implementation of its body var.

  • User interface가 생성되는 시점은 system이 body var 값을 받아오는 시점이다.
  • 이런 방식의 코딩 방식을 'declarative' 라고 부른다.
  • 쓰거나 읽기 쉽고 더 안정적이고 증명 가능한 코드를 구현할 수 있다는 장점이 있다.

증명할 수 있다는 말은 의도한 바를 수행한다는 것을 입증할 수 있다는 의미인가? 이것이 동작하려면 모델에서 변동사항이 생겼을 때 해당 변동사항에 dependency 가 있는 모든 View 에 대하여 body 변수를 요청해야 한다. 이를 관장할 효율적인 시스템이 필요하다.

4) Model에 대한 변동의 결과로서 실제 변경될 수 있는 body var 만 요청해야 한다.

  • 이것이 MVVM 의 전부이며 SwiftUI가 Reactive 하다고 말하는 이유이다.
  • View 는 항상 Model 의 변동 사항에 자동으로 효율적으로 반응한다.

ViewModel의 역할은 Model을 View 에 연결하여 Model 의 변경으로 인해 영향을 받는 부분이 발생하게 될 때 해당 부분의 body var에 접근하여 갱신된 내용을 표시하도록 빌드해주는 것이다.

ViewModel

1) View 를 Model에 바인딩하여 Model의 변경 사항으로 인해 View가 반응(React)하여 다시 Build 되도록 하는 것이다.

  • Model 과 View 사이의 Interpriter (해석) 역할을 할 수 있다.

Memorize Demo App 에서 사용하는 Model은 단순한 struct 구조체이다. 일반적인 Model은 SQL 데이터베이스 혹은 네트워크에서 넘어오는 HTTP 요청일 수도 있기 때문에 꽤나 복잡해질 수 있다.

이전에 우리는 View 코드가 단순해지기를 원하기 때문에 declarative를 활용했던 만큼 복잡한 logic 을 View 와 분리시킬 필요가 있다. 따라서 ViewModel에서 복잡한 모든 작업을 수행한 후에 View에 데이터를 넘겨주어 View 가 복잡해지는 것을 막을 수 있다.

예를들어 ViewModel은 SQL 데이터베이스 모델의 데이터를 View에 배열로 넘겨주거나 일부 Model의 Int를 Float 등 View 가 선호하는 자료형으로 변환하여 넘겨줄 수 있다.

다시말해 View 가 호출할 수 있는 ViewModel의 함수와 변수를 볼 때 View가 작업을 수행하는데 필요한 것과 정확히 일치하도록 설계되어야 한다. -> Interpreter 라고 말하는 이유이다.

  • Model의 Gatekeeper 역할을 하며 특히 모델을 변경할 때 모델에 대한 접근이 정상적으로 작동하는지 확인한다.

위 사항은 언제나 View가 ViewModel에 데이터를 요청하여 Model부터 데이터를 가져와야 한다는 MVVM 의 주요 규칙에 의해 자연스럽게 발생한다. 이를 통해 Reactive 메커니즘에 주요한 영향을 줄 수 있게 된다. 방법은 다음과 같다.

  • ViewModel은 모델의 변경사항을 지속적으로 확인한다.
  • 변동 사항을 감지하면 즉시 변경된 사항을 Entire World에 Publish 한다.
  • 이 정보에 관심있는 모든 사람은 들을 수 있다!

그렇다면 왜 "변동 사항을 공표하는 방식을 채택하였는가?" 에 대한 의문점이 생길 수 있다. ViewModel에는 View 구조체에 대한 정보가 담긴 포인터나 데이터 구조가 존재하지 않는다. 왜냐하면 Model에 접근하기 위해 어떤 VIew 에도 연결할 것을 원하지 않기 때문이다.

정리해서 말하자면 특정 뷰와 연결되어있지 않기 때문이다. 위와 같은 공표를 "듣는" 입장은 주로 View 쪽에 존재하게 되며 ViewModel 이 게시하는 것을 View가 구독한다고 표현한다.

실제 SwiftUI에서 해당 ViewModel의 발표를 구독하는 VIew에 대해 어느 게시 이벤트가 발생했음을 확인하면 View에 body var을 요청하고 다시 그린다.

지금까지는 Model의 변동사항을 ViewModel을 통해 View에 반영시키는 방법에 대하여 생각해 보았다면 반대로 View에서 발생한 변동사항을 Model에 적용시키는 메커니즘에 대해서도 생각해볼 수 있다.

View에서 탭, 스와이프, UI 탐색 등 터치 이벤트에 의해 데이터가 변경되어 Model이 변경 될 수 있을 것이다. 이 때 ViewModel에 사용자의 의도를 처리하는 또 다른 책임을 추가하여 이를 처리한다.

구현방법은 ViewModel에 View 가 다음과 같은 사용자 의도를 처리하는 함수를 정의한다.

방금 사용자가 내 View에서 어느 동작을 했기에 Model에서 다음과 같은 개념적인 일이 발생하기를 의도합니다.

 

ViewModel은 사용자의 의도를 Model에 대한 특정한 수정으로 변환한 후 Model 에 반영한다.

예를 들어 생각해보자

모델에 항공편 혹은 호텔 정보가 있는 여행 어플리케이션에서 일반적으로 발생할 수 있는 사용자의 의도는 이번 휴가를 예약하는 것과 비슷 한 유형일 것이다. 이 상황에서 예약 버튼을 클릭했을 때 사용자의 의도가 데이터의 User id 가 어쩌구 저쩌구 하는 것이 아닐 것이기에 ViewModel에서 bookit과 같은 간단한 함수를 호출한다. 이게 바로 사용자의 의도이기 때문이다.

ViewModel은 View와 Model 간의 소통을 용이하게 하는 책임의 일부로 나머지 다른 모든 작업을 수행하게 된다.

Swift UI가 이와 같이 intent를 생각하는 접근을 강제하는 것은 아니지만(이 방식을 사용하지 않아도 코드가 정상 동작할 수 있다) Intent 방식을 생각하면서 코딩할 것을 권장한다.

View 에서 무슨 일이 발생하고 그것이 모델에 어떻게 영향을 주어야 하는지 생각하는 아주 깔끔한 방법이 될 수 있다!

큰 맥락은 단순함과 선언적 접근이 좋은 VIew 디자인의 핵심이며 좋은 ViewModel 은 해석 기능을 통해 대상을 표현하는 방식뿐만 아니라 View 가 접근할 수 있는 정말 합리적인 의도를 제공함으로써 동작한다.

소프트웨어 공학자들의 깊은 생각을 이해하는 것이 쉽지만은 않다. ㅠㅠ

20:13

반응형