MVC…MVC!!!
- 이전 UIKit을 처음 입문할 때, ViewController라는 것을 처음 접하게 되었다.
- 이후, UIKit 프레임워크를 계속 사용하면서 MVC를 지향하며 설계 되었다는 것을 알기만 했지, 정작 내가 구성하고 있는 코드가 MVC 패턴을 만족하는 지?
- 위 사항에 대한 고민을 계속 하게 되었다.
오늘은 MVC에 대해 알아보면서, 현재 내가 작성한 코드가 MVC를 만족하는 지 알아보려고 한다.
서칭을 통해 알게 된 부분에 저의 생각을 녹여내는 과정에서, 오류가 있을 수도 있습니다.
틀린, 혹은 의아한 부분이 있다면 언제든지 질문 환영입니다.
MVC란?
- 익히 들어봐서 알 수도 있다.
Model - View - Controller
패턴의 줄임말이다.- Model, View, Controller 각 계층은 자신들만의 책임과 역할을 가진다.
대략적인 흐름(Flow)는 아래 그림과 같습니다.
처음 이 그림을 본 소감은 아래와 같다.
View 와 Model 을 ViewController가 상호작용시켜준다.
우선 각각의 계층이 어떤 역할을 하는 지 알아가보자.
1. Model
- Model을 듣는 순간 어떤 생각이 나는가?
- 처음 들었을 때 나는 핸드폰에서 흔히 사용되는
모델명
과 연관지어 떠올리게 되었다. - 위키백과의 정의는 아래와 같다.
- 처음 들었을 때 나는 핸드폰에서 흔히 사용되는
모형은 객체, 시스템, 또는 개념에 대한 구조나 작업을 보여주기 위한 패턴, 계획, 또는 설명이다.
- 나는 이를 보고
Model 계층에선 데이터와 관련된 정보가 담겨있겠다
라는 생각을 했었다.
애플 문서에선 어떻게 정의할까?
- 모델 객체는 애플리케이션에 특정한 데이터를 캡슐화하고 해당 데이터를 조작하고 처리하는 논리와 계산을 정의합니다.
그럼 어떤식으로 사용되어야 할까?
- 공식문서에 아래와 같은 말이 있었다.
Ideally, a model object should have no explicit connection to the view objects that present its data and allow users to edit that data— it should not be concerned with user-interface and presentation issues.
- 즉, Model은 UI와 직접적인 연결이 되지 않아야 한다.
Model로 사용되는 예시는 무엇이 있을까?
[현재 진행하는 나의 과제를 토대로 예시를 작성했다]
- 데이터로 사용하는 구조체
class Rectangle: Shape, AlphaEditable, ColorEditable {
let id: String
var size: Size
var position: Point
var alpha: AlphaNumber
var color: RGBColor
var typeIndex: Int = 0
init(id: String, size: Size, position: Point, alpha: AlphaNumber, color: RGBColor) {
self.id = id
self.size = size
self.position = position
self.alpha = alpha
self.color = color
}
}
- Util, Extension, Constant
enum RGBConstants {
static let value: Double = 255.0
}
enum AlphaConstants {
static let value: Double = 10.0
}
enum SideViewConstants {
static let width: Double = 200
}
enum InitialShapeConstants {
static let width: Double = 150
static let height: Double = 120
}
- Persistance 로직
- 네트워크, 데이터 파싱 로직
- Manager 객체
protocol은 Model이 아니다 (Interface로 취급)
2. View
- 쉽게 말해, 사용자가 볼 수 있는 애플리케이션의 객체이다.
- 이들의 주요 목적은 애플리케이션의 모델 객체에서 데이터를 표시하고, 해당 데이터를 편집할 수 있도록 하는 거다.
- View 객체는 일반적으로 MVC 애플리케이션의 모델 객체와 분리된다.
- 그치만, 무조건 데이터를 저장해서 안되는 것은 아니고, 간단한 정보들은 View 단에서도 정의해서 사용할 수 있다.
재사용
이 매우 중요.- 내가 이를 이해하기론, Component로 제작해서 여러 상황에서도 재사용할 수 있게끔 하는 것이 중요하다고 느껴졌다.
예시로, 현재 수행중인 과제에서 사용되는 CustomButton View이다.
- 이를, 생성할 때 정보를 바탕으로 Rectangle, Photo, Text Button 으로 사용할 수 있다.
final class CustomButton: UIView {
// MARK: - Private Properties
private let button: UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
private let iconImageView: UIImageView = {
let imageView = UIImageView()
imageView.tintColor = .darkGray
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
private let titleLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 14)
label.textColor = .darkGray
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
// MARK: - Initialization
init(frame: CGRect, icon: UIImage?, title: String) {
super.init(frame: frame)
setUp(icon: icon, title: title)
makeAutoLayout()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUp(icon: nil, title: "")
makeAutoLayout()
}
// MARK: - Public Methods
func updateIcon(_ icon: UIImage?) {
iconImageView.image = icon
}
func updateTitle(_ title: String) {
titleLabel.text = title
}
func addTarget(_ target: Any?, action: Selector, for controlEvents: UIControl.Event) {
button.addTarget(target, action: action, for: controlEvents)
}
}
3. ViewController?
- 애플리케이션의 하나 이상의 뷰 객체와 하나 이상의 모델 객체 사이의 중개자 역할을 수행한다.
- 애플 공식 문서는 다음과 같이 설명한다.
- Controller objects are thus a conduit through which view objects learn about changes in model objects and vice versa. Controller objects can also perform setup and coordinating tasks for an application and manage the life cycles of other objects.
- [따라서 컨트롤러 객체는 뷰 객체가 모델 객체의 변경 사항을 학습하고 그 반대의 경우도 마찬가지인 통로입니다. 컨트롤러 객체는 애플리케이션의 설정 및 조정 작업을 수행하고 다른 객체의 수명 주기를 관리할 수도 있습니다.]
- 위와 같이 해석할 수 있는데 조금 쉽게 풀어 이야기를 하자면
- ViewController의 역할
- View로부터 유저와의 상호작용에 대한 정보를 받고, 해당 로직을 실행하고 Model의 정보를 업데이트하는 기능쉽게 설명하자면
- 사용자가 버튼을 누름 → ViewController가 이 동작을 감지함.
- 이때, 사용자의 행동에 따라 어떤 비즈니스 로직이 실행됨. 예를 들어, 사용자가 “저장” 버튼을 눌렀다면, 사용자가 입력한 정보를 저장하는 로직이 동작함.
- 이 로직이 끝나면 Model을 업데이트함. 즉, 새 데이터를 저장하거나 기존 데이터를 변경함.
// 사용자가 버튼을 눌렀을 때 호출되는 함수 @IBAction func saveButtonPressed(_ sender: UIButton) { // ViewController가 View로부터 사용자의 입력을 받아옴 let newName = nameTextField.text // Model에 새 데이터를 업데이트 person.name = newName }
- 예를 들어, 사용자가 버튼을 눌러 데이터를 저장하거나 변경할 때 그 상호작용을 감지한 후 Model에 해당하는 데이터를 수정하는 역할을 ViewController가 담당.
- View에서 보여주기 위한 데이터를 이 Controller가 보내주면서 View를 refresh하고, 그 데이터를 Model로부터 가져오는 기능쉽게 설명하자면
- Model에는 실제 데이터가 저장되어 있음. 예를 들어, “사용자의 이름”이 Model에 저장.
- ViewController는 Model에서 데이터를 가져와 이를 View에 전달하여 화면에 표시.
- 예를 들어, 사용자의 이름을 UILabel에 표시할 때, ViewController가 Model에서 사용자의 이름을 가져와 View를 업데이트.
// ViewController가 Model로부터 데이터를 가져와서 View를 업데이트하는 함수 func updateView() { // Model에서 데이터 가져오기 let personName = person.name // View에 데이터를 전달 (업데이트) nameLabel.text = personName }
- 예를 들어, Model에 저장된 데이터를 화면에 표시하기 위해서 View에 넘겨줍니다.
- View로부터 유저와의 상호작용에 대한 정보를 받고, 해당 로직을 실행하고 Model의 정보를 업데이트하는 기능쉽게 설명하자면
그래서 Model은 어떻게 View와 Communication을 할 수 있지?
- 위의 그림과 예시에서 보았 듯, ViewController를 통해 View와 상호작용을 진행한다.
전체적인 예시로 뭐가 있을까?
1. 사람 정보를 나타내는 Person 구조체가 있다.
struct Person {
let name: String
let age: Int
}
2. Model 데이터를 받아와서 화면에 표시하는 역할을 하는 PersonView가 있다.
import UIKit
class PersonView: UIView {
private let nameLabel = UILabel()
private let ageLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
nameLabel.translatesAutoresizingMaskIntoConstraints = false
ageLabel.translatesAutoresizingMaskIntoConstraints = false
addSubview(nameLabel)
addSubview(ageLabel)
NSLayoutConstraint.activate([
nameLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 20),
nameLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor),
ageLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 10),
ageLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Model 데이터를 받아와 View에 반영
func configure(with person: Person) {
nameLabel.text = "Name: \(person.name)"
ageLabel.text = "Age: \(person.age)"
}
}
3. Model ↔ View 사이에서 상호작용을 담당하는 ViewController가 있다.
import UIKit
class ViewController: UIViewController {
// Model: Person 인스턴스 생성
let person = Person(name: "John Doe", age: 30)
override func viewDidLoad() {
super.viewDidLoad()
// PersonView 설정
let personView = PersonView(frame: CGRect(x: 0, y: 0, width: 200, height: 100))
personView.center = view.center
view.addSubview(personView)
// Model 데이터를 View에 전달하여 표시
personView.configure(with: person)
}
}
추가적인 궁금즘
- 그렇다면 ViewController에서 View를 정의하고 쓰는 것은 왜 되는 것인가?
- ViewController는 Model과 View의 상호작용의 목적만을 가지고 있는 것 아닌가?
- etc
- ViewController에서 AutoLayout을 선언하는 과정
- ViewController에서 UIView를 선언한 뒤 이에 대한 속성을 UIViewController가 가지는 view에 넣어주는 것
- 위에 질문에 이어서
- (모델이 필요한) 어떤 화면을 생성하고 싶다면
- (Custom) View, 이에 해당되는 ViewController, Model 이렇게 3가지로 만들어야 하는지?
다음번에 이에 대한 궁금즘에 대해 해소해 볼 예정이다.
'iOS > UIKit' 카테고리의 다른 글
UITextFieldDelegate와 알아보는 Delegate Pattern (0) | 2024.08.22 |
---|---|
ViewController 생명주기 (0) | 2024.08.21 |
UIKit이란? (0) | 2024.08.21 |
UIKit No.3 (0) | 2024.01.14 |
UIKit No.2 (0) | 2024.01.11 |