[ Swift ] MVC ? MVVM ?

2020. 10. 26. 22:28Terminology

MVC

MVC( Model View Controller ) 

Model : Data | Struct

View : UI요소 | UIView

Controller : 중계자 | UIViewController

 

그런데 !! Controller가 하는 일이 너무 너무 많아 ! 그래서

Massive View Controller  

MVC를 이렇게^ 부르기도 해....

 

그래서 더 나은 방법인 !

MVVC

MVVM( Model View ViewModel )

를 사용할거야 !

기술부채를 줄이고자 우리는 리펙터링[ 코드수정( 재사용 가능. 유지보수 적은 노력으로 ) ] 을 할건데.

규칙이 있어 !

 

중복제거, 

단일책임 갖기.

 

예를 들어 설명하자면

 

 public var ageText: String {

    let today = calendar.startOfDay(for: Date())

    let birthday = calendar.startOfDay(for: pet.birthday)

    let components = calendar.dateComponents([.year], from: birthday, to: today)

    let age = components.year!

    return "\(age) years old"

  }

우리는 이렇게  today , birthday, components 들에게 일일이 값을 부여했어 !  calendar을 이용해서 말이야...

 

그런데 우리가 코드를 계속 유지하기엔 이렇게 일일이 값을 넣어주는게 상당히 불편한 방법이야! 

그래서 우리는 ViewModel을 이용해서 한번에 는 아니더라도 더 쉽고 간편하게 값을 넣고 싶어 ! 

그게 유지보수에 좋겠지 ? 일일이 한줄한줄씩 고치는것보다. 자동화(?) 처럼 딱딱 ! 값이 넣어지게 말이야

 

그래서 우리는 리펙터링! 용어가 생소하다면 이걸 코드 수정이라고 생각하면 돼 ㅎㅎ

리펙터링을 해서 아까 말했듯이 중복제거, 단일책임을 갖게 할거야.


import UIKit

class BountyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    let viewModel = BountyViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    //...
}
class ListCell: UITableViewCell {
    //...    
    func update(info: BountyInfo){
        imgView.image = info.img
        nameLabel.text = info.name
        bountyLabel.text = "\(info.bounty)"
    }
}
struct BountyInfo
{
    let name: String
    let bounty: Int
    
    var img: UIImage? {
        return UIImage(named: "\(name).jpg")
    }
    
    init(name: String, bounty: Int){
        self.name = name
        self.bounty = bounty
    }
}

class BountyViewModel {
    let bountyInfoList: [BountyInfo] = [
        BountyInfo(name: "brook", bounty: 33000000),
        BountyInfo(name: "chopper", bounty: 50),
        BountyInfo(name: "franky", bounty: 44000000),
        BountyInfo(name: "luffy", bounty: 300000000),
        BountyInfo(name: "nami", bounty: 16000000),
        BountyInfo(name: "robin", bounty: 8000000),
        BountyInfo(name: "sanji", bounty: 77000000),
        BountyInfo(name: "zoro", bounty: 120000000)
    ]
    var sortedList: [BountyInfo] {
        let sortedList = bountyInfoList.sorted { prev, next in
            return prev.bounty > next.bounty
        }
        return sortedList
    }
    var numOfBountyInfoList: Int {
        return bountyInfoList.count
    }
    func bountyInfo(at index: Int) -> BountyInfo {
        return sortedList[index]
    }
}

무슨 코드인지는 모르겠지만 정리가 돼있는 느낌이지 않니? 아니면 말고...

 

func update(info: BountyInfo){            

        imgView.image = info.img            

        nameLabel.text = info.name            

        bountyLabel.text = "\(info.bounty)"

    }                                                           

이 부분을 봐봐. 

아까 우리는 직접 Model(데이터, ex. calendar )에 접근해서 일일이 값을 넣어줬었잖아

그런데 우리는 지금 넘겨받은 BountyInfo를 이용해서 info에서 이미지, 이름, 현상금을 저장했어.

만약에 데이터에서 직접 값을 가져와야 했다면 우리는 해당하는 코드들을 찾아가면서 하나하나씩 값을 바꿔줘야 했을거야

이해가 안간다고?

만약에 bountyInfo에 항목이 하나 더 늘었다고 생각해봐 ! 

생년월일이 담긴 항목이 늘었다고 생각해보자. 그럼 우리는 Cell에 데이터를 추가하기위해서 해당하는 데이터를 일일이 넣어줘야 했을거야 ! 번거롭지?

하지만 우리는 리펙터링을 함으로서 클래스에 있는 리스트의 항목만 바꿔주고 

birthdayLabel.text = "\(info.birth)"

이 한줄만 더 넣어주면 값을 일일히 다 안넣어줘도돼 ㅎㅎ

 


10, 200 법칙이란게 있어 method는 10줄, class는 200줄 이내로 ㅎㅎ 어려울거야. 하지만 지키도록 해보자

30, 400 법칙을 먼저 따라도 좋아.