CS/디자인패턴

[디자인패턴] MVC 패턴 with iOS

유정주 2022. 7. 20. 14:59
반응형

MVC 패턴

디자인 패턴 중 가장 기본적인 것을 말하라면 MVC 패턴이라고 할 수 있습니다.

 

MVC 패턴은 애플에서 기본적으로 지원하는 디자인 패턴으로,

Model - View - Controller 구조의 아키텍처 패턴을 말합니다.

 

일반적으로 사용되는 MVC 패턴과는 달리 iOS에서는 조금 다르게 사용이 되는데요.

iOS는 ViewController가 사용되기 때문에 View와 Controller가 결합되었습니다.

 

일반적인 MVC 패턴과 애플의 MVC 패턴을 비교하고, MVC 장단점을 알아보도록 합시다.

마지막에는 직접 구현해보는 시간을 갖겠습니다.

 

일반적인 MVC 패턴

일반적인 MVC 패턴은 간단히 알아봅시다.

 

 

Model은 앱이 가지는 데이터와 비즈니스 로직을 갖고 있습니다.

View는 앱에서 유저에게 보이는 역할을 합니다. 비즈니스 로직을 포함하지 않으므로 재사용될 수 있습니다.

Controller는 View와 Model을 잇는 역할입니다.

 

Controller는 View에서 유저의 액션이 발생하여 데이터 변경이 이루어졌음을 Model에게 알립니다.

Model은 변경된 데이터를 기반으로 View에게 업데이트 하라고 Controller를 통해 알립니다.

 

View는 Model의 데이터를 보여주고, Model은 데이터를 관리하는 것입니다.

Controller는 View와 Model을 잇는 다리 역할이고요.

 

애플의 MVC 패턴

이제 애플의 MVC 패턴에 대해 알아봅시다.

MVC 패턴은 애플이 UIKit에서 채택하기로 한 디자인 패턴이라 iOS에서는 가장 일반적입니다.

 

ViewController를 사용하기 때문에 View와 Controller를 결합시켜 ViewController와 Model로 구분됩니다.

Controller가 View의 Life Cycle까지 관리하면서 Controller의 역할이 더 늘어났습니다.

하지만 View와 Model의 연결은 더 간편해졌죠.

원래는 Controller를 거쳐 View에게 전달해야 했는데 그냥 ViewController에게 전달하면 되니까요.

 

결론은 애플의 MVC는 View와 Controller를 결합한 것일 뿐,

MVC의 근본은 동일하다고 정리할 수 있겠습니다.

 

MVC 구조 정리 및 장단점

Model, View, Controller에 대해 다시 한 번 정리해보겠습니다.

 

Model

모델은 앱의 데이터를 정의하고 있으며 비즈니스 로직을 포함하고 있습니다.

구조체나 클래스로 구현되어 있습니다.

비즈니스 로직, 네트워킹 등을 중심으로 테스트할 수 있습니다.

 

사용 예시

- Network Code : 네트워크 통신은 단일 클래스에서 사용하는 것이 좋습니다. HTTP 헤더, 응답 및 오류 처리 등 모든 네트워크 코드를 추상화하여 구현합시다.

- Persistence Code : 데이터베이스, 코어 데이터, 디바이스 데이터를 저장할 때 사용합니다.

- Parsing Code : 네트워크 response를 parsing하는 JSON Codable 모델을 정의할 때 사용합니다.

- Constants : 상수를 모델로 정의하면 유용합니다. Storyboard 이름, 날짜 formatter, 색상 등을 여러 곳에서 재사용할 수 있습니다.

- Helpers와 extensions : 프로젝트에서 String 등의 기능 추가도 모델을 사용합니다.

 

View

View는 사용자가 보는 화면을 그리며 사용자와 앱의 상호 작용을 담당합니다.

모델과 소통해도 안 되고 어떠한 비즈니스 로직도 포함되면 안 됩니다.

UI와 관련 없는 코드도 포함되지 않을수록 좋습니다.

 

사용 예시

- UILable, UIButton, UIImage 등

 

Controller

View와 Model을 중개하는 역할입니다.

View로부터 사용자의 Action을 받아 Model에게 어떤 작업을 해야할지 알려주거나,

Model의 데이터 변화를 View에게 전달하여 View를 업데이트 해야 하는지를 알려줍니다.

 

MVC 장점

MVC의 가장 큰 장점은 생산성이 높고 쉽다는 것입니다.

 

MVC는 각 구조의 역할이 명확하므로 역할을 분담하여 빠르게 구현할 수 있습니다.

다른 패턴에 비해 코드량이 적으며 많은 개발자에게 친숙하기 때문에 쉽게 접근, 유지보수할 수 있습니다.

 

따라서 프로젝트 규모가 크지 않고, 특별한 패턴이 필요하지 않을 때 MVC를 사용하면 빠르고 쉽게 개발할 수 있습니다.

 

MVC 단점

MVC의 가장 큰 단점은 테스트가 힘들고 Controller의 크기가 크다는 것입니다.

 

View와 Controller가 결합되어 Controller가 View의 역할도 할 수 있습니다.

따라서 분리하기 어렵고 재사용성이 떨어집니다.

유닛 테스트를 진행하기 힘들어지고 내부 구조가 복잡해질 수 있습니다.

 

MVC를 지키며 개발하는 5가지 방법

MVC를 지키며 개발하는 방법을 알아보겠습니다.

 

1. Model은 Controller와 View에 의존하면 안 된다.

Model 내부에 View와 Controller와 관련된 코드가 없어야 합니다.

 

2. View는 Model에만 의존하고 Controller를 의존하면 안 된다.

View 내부에 Model의 코드만 존재하고 Controller 코드는 존재하면 안 됩니다.

 

3. View가 Model로부터 데이터를 받을 때는 사용자마다 다르게 보여줘야 하는 데이터만 받아야 한다.

Title 같은 공통 문구는 Model에서 받지 않아야 하고 사용자 이름, 닉네임처럼 사용자마다 다른 데이터만 Model로 부터 받아야 합니다.

 

4. Controller는 View와 Model에 의존해도 된다.

Controller 내부에 Model과 View 코드가 존재하도 됩니다.

 

5. View가 Model로부터 데이터를 받을 때는 Controller를 통해 받아야 한다.

Controller는 View와 Model을 중개하므로 View는 Controller로부터 Model의 데이터를 받아야 합니다.

 

MVC 실습

iOS 환경에서 MVC 패턴으로 구현해봅시다.

MVC 전체 코드는 아래 링크에서 확인할 수 있습니다.

 

GitHub - jeongju9216/DesignPattern: Swift 디자인 패턴 연습

Swift 디자인 패턴 연습. Contribute to jeongju9216/DesignPattern development by creating an account on GitHub.

github.com

 

 

맨 위에는 고정된 문자열을 출력하는 UILabel이,

가운데에는 User의 이름이 출력되는 UILabel이,

맨 아래에는 눌렀을 때 가운데 이름이 변경되는 UIButton이 존재합니다.

 

Model

모델은 User에 관한 내용이 있겠죠?

struct User {
    let name: String
    let age: Int
}

User 모델은 struct를 이용했습니다.

User는 name과 age 프로퍼티를 가집니다.

 

struct UserGroup {
    var users: [User] = [
        User(name: "유정주", age: 24),
        User(name: "시리", age: 1),
        User(name: "애플", age: 50)
    ]
}

이건 좀 애매한 부분인데요.

Controller에 배열을 넣는게 좀 안 맞는다고 느껴서 UserGroup이라는 구조체를 따로 생성했습니다.

혹시 이부분에 대한 의견이 있으시다면 댓글로 알려주시면 감사하겠습니다.

 

View

View는 화면에 보여지는 코드를 작성해야 합니다.

저는 ViewController의 view를 Custom View로 따로 구현하여 교체했습니다.

 

class View: UIView {
    
    //MARK: - Views
    private var titleLabel: UILabel!
    private var nameLabel: UILabel!
    var nextButton: UIButton!

    ...
    
    //MARK: - Methods
    func changeName(name: String) {
        nameLabel.text = name
    }
    
    ...
}

화면을 구성하는 View들을 생성하고

Controller에서 name String을 전달하여 label의 text를 변경할 수 있는 메서드를 정의했습니다.

 

View를 따로 생성하면 ViewController의 코드가 줄어드는 장점이 있습니다.

이렇게 생성한 View는 Controller 부분에서 ViewController의 view로 사용됩니다.

 

Controller

Controller에서는 View와 Model을 이어줘야 합니다.

 

//MARK: - Views
private var customView: View!

//MARK: - Life Cycles
override func loadView() {
    super.loadView()

    customView = View(frame: self.view.frame)
    self.view = customView
}

override func viewDidLoad() {
    super.viewDidLoad()

    customView.changeName(name: userGroup.users[count].name)
    ...
}

먼저 View 코드입니다.

위에서 생성한 View를 loadView()에서 ViewController의 view로 설정합니다.

맨 처음에는 모델의 첫 번째 데이터로 이름을 설정합니다.

 

private let userGroup: UserGroup = UserGroup()

Model 코드입니다.

User 배열이 담겨 있는 구조체인 UserGroup을 생성하여 데이터를 가져옵니다.

 

@objc func clickedNextButton(sender: UIButton) {
    count += 1
    count = count >= userGroup.users.count ? 0 : count

    customView.changeName(name: userGroup.users[count].name)
}

Controller는 Next 버튼을 누르면 View에 Model의 다음 데이터를 전달하여 View를 갱신합니다.

 

성공적으로 View와 Model 사이를 이어주고

View는 Controller를 거쳐 Model의 데이터를 전달 받아 화면을 갱신하였습니다.

 

마무리

오늘은 디자인 패턴 중 가장 기본이라고 할 수 있는 MVC 패턴에 대해 알아보았습니다.

작은 프로젝트를 진행할 때 다른 디자인 패턴보다 빠르고 쉽게 할 수 있으니 익혀두면 매우 유용할 것입니다.

 

감사합니다!

 

참고

https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html

https://pinelover.tistory.com/m/313

https://rldd.tistory.com/366?category=1036241 

https://coding-sojin2.tistory.com/entry/Model-View-Controller


잘못된 점이 있다면 댓글로 알려주시면 감사하겠습니다.

감사합니다.

반응형