개발삽질기/iOS

CS193P 3편,4편 정리 (2) : MVVM으로 앱 만들기

세밍_ 2023. 7. 2. 16:36
728x90
반응형

일단... 4강까지 듣고 난 후기,,

와 처음부터 CS193P를 들을걸,, 원리를 알려주니까 정말 이해하기 쉽다. 

이제 나 진짜 앱개발이 뭔지 알겠다. 

----

3줄 요약

- Model : 앱의 데이터와 동작을 관리함 -> Struct이지만 데이터와 동작을 관리해서 변할 수 있기 때문에 Mutating을 사용함 

- View : 데이터의 변화와 유저가 사용함에 따른 동작에 대한 반응을 뷰에서 보여줌 - ObservedObject로 뷰 모델을 관찰함

- ViewModel : 뷰와 모델 사이에서 데이터와 동작하는 것들을 전달함 -> ObservableObject와 Published model을 사용함 

 

----

1,2편에서 view를 만들어줬으니까 Model과 ViewModel을 만들어줘야 한다. 

Model은 struct로 만들어 준다. 

모델을 만들때는 무엇을 하는 모델인지 생각해줘야 한다. 

기본적으로 card라는 어레이가 필요한데, 이 array는 Card라는 것을 담아줄 것이다. 

우리 앱은 Card라는 것을 사용하기 때문에 card라는 구조체를 만들어 줄 것이다.

 

여기서 private(set)은 읽기만 가능하고 변경은 불가능하다는 것인데, 외부에서 cards를 볼 수는 있지만 Card Stuct 구조는 바꿀 수 없음을 의미한다. 

그 Card는 모델 안에 구조체로 만들어 준다. 

isMatched, isFacedup, content 성질이 필요한데, isMatched, isFacedUp은 Bool type으로 기본값도 주어주자

content는 string도, int도 될 수 있어서 어떤 형식이든 상관없는 generic 타입으로 만들어주자. 

이때 generic type으로 만드는 것이기 때문에 model명 뒤에 generic 타입을 쓴다고 명시해 줘야 한다. 

 

메모리 게임 모델에서 카드를 고르는 행위를 하기 때문에 

func choose (_ card : Card)

를 만들어주는데, 이때 _는 함수를 호출할때 레이블을 표시하지 않겠다는 얘기이다. 똑같은 card, card가 반복되니 더 쉽게 읽힐 수 있기 위함니다. 

 

메모리 게임을 만들때 카드가 몇개가 있어야 하는지, 컨텐츠는 어떤것인지 알아야하는데 그것을 init으로 만들어 줘야 한다. 

Model을 만들었으면 View에 반영할 수 있는 ViewModel을 만들어줘야 한다. 

뷰 모델은 클래스로 만든다. 

만약에 서버가 있어서 모델이 따로 있거나 하면 model을 별도로 만들어 줄 필요가 없는데,

여기서는 그렇지 않기 때문에 model을 별도로 선언해준다.

 

이때 MemoryGame<CardContent>의 타입을 썼다면, EmojiMemoryGame 에서는 content가 String이기 때문에 MemoryGame<String>으로 모델을 만들어준다. 

 

게임을 만들어주려고 MemoryGame에서 가져온 init을 사용해서 초기화 해주려고 하는데 다음과 같은 오류가 뜬다.

Cannot use instance member 'emojis' within property initializer; property initializers run before 'self' is available

 

이 오류는 속성 초기화 중에 이모지를 호출할 수 없다는 뜻으로 전역적인 뭔가가 필요하다는 얘기인데, 프로퍼티를 전역으로 빼기에는 문제가 많다. 그래서 전역의 의미를 내포하는 static을 사용해서 변수를 이 안에서 전역적인 역할을 해주게끔 만들어준다. 

static은 전역 역할을 하지만 static이 붙은 프로퍼티를 static 프로퍼티라고 하지 않고 type 프로퍼티라고 말하는데, 그것은 상위 struct, class에 속했다는 것을 알려주기 위함이다. 

 

뷰모델을 만들었으니 데이터의 흐름, 변환은 모두 뷰모델이 관리할 것이다. 

뷰는 이제 데이터를 가지고 있을 필요가 없다. 

기존의 뷰에서 있는 데이터들을 모두 없애주고 CardView에 모델의 Card를 선언해 줘서 그 객체를 사용한다. 

이때 card는 변경 불가능한 것이기 때문에 let으로 선언을 해줘야 한다.

이제 뷰의 동작 방법은 모두 모델에서 선언을 해줄 것이다. 

하지만 뷰는 원래 불변하는데, 동작방법을 설명하면서 뷰의 데이터가 변할 수 있는 부분이기 때문에 mutating을 써줘야 한다. 

그리고 이때 옵셔널이라는 것을 쓰는 데,

옵셔널은 값이 있음과 값이 없음 이 2개가 있는 enum형태로, 값이 없을 수 있는 상황을 잘 대처하게 해준다. 

옵셔널의 기본 값은 nil이기 때문에 옵셔널 값을 사용하려면 옵셔널을 벗겨줘야 하는데, 

옵셔널을 벗길 수 잇는 방법에 3가지가 있다. !를 사용하는 강제 옵셔널 언래핑 방법은 벗기려는 대상이 무조건 값이 있음이 확고해야지만 사용해야 한다. 아니면 오류가 발생하게 된다. If let을 사용하는 방법이 있는데, 이 방법은 옵셔널일때 return을 작성하는 것이 필수는 아니다. gaurd let을 사용하는 방법도 있는데, 이 방법은 옵셔널일때 return을 작성하는 것이 필수이다. 

뷰모델에서 데이터가 변경됨을 감지하면 뷰에서는 이를 재빠르게 반영할 수 있어야 하는데, 이걸 ObservableObject, Published, ObservedObject로 관리한다. 

뷰모델을 ObservableObject로 만들면서, 변화함을 게시할 수 있는 객체로 만든다. 

그리고 뷰 모델에서 어떤 것들이 변경되었는지 게시해야 한다. 

objectWullChange.send()를 변화하는 곳마다 적어주는 방법이 있지만, @Published로 모델을 선언해주면서 모델이 변할때마다 자동적으로 objectWullChange.send()를 수행할 수 있다. 

그리고 뷰에서는 Object를 관찰하고 있기 때문에 ObservedObject로 뷰모델을 선언해준다. 

----

추가)

struct안에서 만들어지는 요소는 ==로 비교할 수 없는게, Equtable이라는 프로토콜을 준수하지 않는다고 해서 그런데, 만약에 ==, != 을 사용하고 싶다면 equtable을 사용해줘야 한다. 

728x90
반응형