MVC 패턴(Model, View, Controller)
객체지향 프로그래밍 안에서는 서로 메시지를 주고 받으며 일처리를 한다.
객체를 너무 중구난방으로 만들어놓으면 객체끼리 충돌이 일어나는 현상이 있을 수 있다.
우리가 두개의 식당을 운영하는 사장이라고 해보자.
1. 역할 분담을 하나도 안해준 식당(설거지도 했다가 조리도 했다가 서빙도 하는 식당)
2. 역할 분담이 명확히 되어 있는 식당(누구는 설거지, 누구는 조리, 누구는 서빙 담당)
두 식당 모두 직원 중 한명이 아프다고 가정하자
1번 식당의 경우 대타를 구하면 어떤 일을 지시해야하는지도 모호하고,
이 일 저 일 다 해야해서 대체하기가 힘들어진다.
2번 식당의 경우는 조리 직원이 아프면 조리 직원의 대타를 구하면 되고,
설거지하는 직원이 아프면 설거지하는 직원의 대타를 구하면 된다.
훨씬 수월하다.
프로그래밍도 그와 같다. 역할을 분리해줄 수록 유지보수하기에 수월해진다.
역할 분리를 해주는 방법은 여러가지이고, 그 중 하나가 MVC 패턴이다.
⎮ MVC 패턴이 뭔데?
예? MBC요?

아니요. MVC요
MVC 패턴을 검색해보면, 다음과 같은 내용을 찾아볼 수 있다.
모델-뷰-컨트롤러(model–view–controller, MVC)는 소프트웨어 공학에서 사용되는 소프트웨어 디자인 패턴이다. 이 패턴을 성공적으로 사용하면, 사용자 인터페이스로부터 비즈니스 로직을 분리하여 애플리케이션의 시각적 요소나 그 이면에서 실행되는 비즈니스 로직을 서로 영향 없이 쉽게 고칠 수 있는 애플리케이션을 만들 수 있다. MVC에서 모델은 애플리케이션의 정보(데이터)를 나타내며, 뷰는 텍스트, 체크박스 항목 등과 같은 사용자 인터페이스 요소를 나타내고, 컨트롤러는 데이터와 비즈니스 로직 사이의 상호동작을 관리한다.
아래의 이미지를 보면 이해가 쉽게 될 수 있다.

모델(Model)은 결국 데이터 계층이다.
유저 모델이 있다고 하면 '이름', '이메일', '전화번호', '주소' 등등이다.
뷰(View)는 말 그대로 View다.
버튼, Text Field 등등의 사용자에게 보여지는 UI를 말한다.
컨트롤러(Controller)는 발생하는 이벤트들을 제어 하는 역할,
즉 Model과 View의 중간 다리 역할을 하는 것이다.
컨트롤러는 뷰와 모델 사이의 중개자 역할을 하며, 특정 트리거를 수행하고 특정 이벤트를 수행한다.
컨트롤러가 유저의 이벤트를 인지한 다음 데이터가 필요한지 여부를 알려주고 처리한다.
⎮ MVC로 구분하면 뭐가 좋은데?
- 재사용성이 높아진다
- 리팩토링 비용이 낮아진다
- 빠르다(단, 간결한 프로젝트에 적합한 패턴)
버튼을 누르면 다음 화면으로 넘어가는 프로그램이 있다고 해보자.

Model, View, Controller의 구분 없이 ViewController에서만 작업한 코드는 다음과 같다.
class ViewController: UIViewController {
// 첫번째 뷰에서 사용하는 버튼
let button = UIButton()
// 두번째 뷰 생성
let nextView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black // 첫번째 뷰 배경 지정
setButton() // 버튼 세팅
buttonTapped()
}
// 첫번째 뷰에 있는 버튼 만드는 함수
func setButton() {
button.setTitle("Next", for: .normal)
button.setTitleColor(.black, for: .normal)
button.backgroundColor = .white
button.layer.cornerRadius = 8
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.widthAnchor.constraint(equalToConstant: 100),
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
// 버튼이 눌리면 동작하는 메서드
func buttonTapped() {
button.addTarget(self, action: #selector(changeView), for: .touchDown)
}
// 다음 뷰 만드는 함수
func makeNextView() {
nextView.backgroundColor = .white
setLabel()
}
// 다음 뷰로 바꿔주는 함수
@objc
func changeView() {
self.view = nextView
makeNextView()
}
// 다음 뷰의 Label을 세팅해주는 함수
func setLabel() {
let title0 = UILabel()
let title1 = UILabel()
title0.text = "짱구"
title0.font = .boldSystemFont(ofSize: 30)
title1.text = "맹구"
title1.font = .boldSystemFont(ofSize: 30)
[title0, title1].forEach {
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
NSLayoutConstraint.activate([
title0.centerXAnchor.constraint(equalTo: view.centerXAnchor),
title0.centerYAnchor.constraint(equalTo: view.centerYAnchor),
title1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
title1.centerYAnchor.constraint(equalTo: title0.centerYAnchor, constant: 30)
])
}
}
지금의 코드는 정상 빌드는 되지만 다음의 문제가 있다.
1. ViewController의 역할이 너무 많다.
- UI 그리기, 화면 전환하기, 데이터 관리하기
2. 재사용성이 떨어진다.
- 첫번째 View와 두번째 View가 묶여 있어서 개별 관리가 어려움
3. 유지 보수가 어렵다.
- 데이터 수정이 필요할 때 뷰 로직도 함께 건드려줘야함.
⎮ 어떻게 하는건데?
이제 하나로 합쳐져 있던 코드를 나눠보자.
// Model.swift
// 데이터를 담아두는 곳
struct User {
let name: String
}
//ViewController.swift
class ViewController: UIViewController {
// 첫번째 뷰에서 사용하는 버튼
let firstView = FirstView()
let secondView = SecondView()
let user1 = User(name: "맹구")
let user2 = User(name: "짱구")
override func viewDidLoad() {
super.viewDidLoad()
self.view = firstView
buttonTapped()
}
// 버튼이 눌리면 동작하는 메서드
func buttonTapped() {
firstView.button.addTarget(self, action: #selector(changeView), for: .touchDown)
secondView.getName(user1.name, user2.name)
}
// 다음 뷰로 바꿔주는 함수
@objc
func changeView() {
view = secondView
}
}
// FirstView.swift
class FirstView: UIView {
let button = UIButton()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .black
setButton()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
// 첫번째 뷰에 있는 버튼 만드는 함수
func setButton() {
button.setTitle("Next", for: .normal)
button.setTitleColor(.black, for: .normal)
button.backgroundColor = .white
button.layer.cornerRadius = 8
self.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.widthAnchor.constraint(equalToConstant: 100),
button.centerXAnchor.constraint(equalTo: self.centerXAnchor),
button.centerYAnchor.constraint(equalTo: self.centerYAnchor)
])
}
}
// SecondView.swift
class SecondView: UIView {
let title0 = UILabel()
let title1 = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
makeNextView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
// 다음 뷰 만드는 함수
func makeNextView() {
self.backgroundColor = .white
setLabel()
}
// 다음 뷰의 Label을 세팅해주는 함수
func setLabel() {
title0.font = .boldSystemFont(ofSize: 30)
title1.font = .boldSystemFont(ofSize: 30)
[title0, title1].forEach {
self.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
NSLayoutConstraint.activate([
title0.centerXAnchor.constraint(equalTo: self.centerXAnchor),
title0.centerYAnchor.constraint(equalTo: self.centerYAnchor),
title1.centerXAnchor.constraint(equalTo: self.centerXAnchor),
title1.centerYAnchor.constraint(equalTo: title0.centerYAnchor, constant: 30)
])
}
func getName(_ name1: String, _ name2: String) {
title0.text = name1
title1.text = name2
}
}
다음과 같이 코드를 분리해서 관리하면,
유지보수가 용이해진다.
(작성 중)
'스파르타코딩 클럽 > 기초' 카테고리의 다른 글
| Swift | 네트워크 쉽게 이해하기(with URLSession) (0) | 2025.04.17 |
|---|---|
| Swift | 클로저(Closure) 쉽게 이해하기? (2) | 2025.04.05 |
| Swift | Delegate 패턴이 뭔데? (0) | 2025.04.02 |
| 객체지향 프로그래밍이 뭔데? + SOLID 원칙 (0) | 2025.03.23 |
| MarkDown(마크다운)이 뭔데? + ReadME 작성하는 법 (0) | 2025.03.16 |