์๋ ํ์ธ์ Foma๐ ์ ๋๋ค!
์ค๋์ Model - View - Presenter๋ก ์ด๋ค์ ธ์๋ M.V.P ๋์์ธ ํจํด์ ๋ํด์ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์ง๊ธ๋ถํฐ ์์ธํ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
MVP๋?
Model - View - Presenter ๋ก ์ด๋ค์ง ๋์์ธ ํจํด์ด๋ฉฐ MVC์์ Controller๊ฐ ํ๋ ์ญํ ์
Presenter๊ฐ ํ๋ค๊ณ ๋ณด๋ฉด ๋ฉ๋๋ค.
"๊ทธ๋ฌ๋ฉด MVC ์ฐ๋ฉด ๋์ง ์ MVP๋ฅผ ์จ?"
MVC๋ Model๊ณผ View๊ฐ ์๋ก ์ฐ๊ฒฐ๋์ด ์์ด ์์กด๊ด๊ณ๋ฅผ ๊ฐ๊ฒ ๋ฉ๋๋ค.
ํ์ง๋ง MVP๋ Model๊ณผ View ๋ถ๋ฆฌ๋์ด ์๊ณ ์ค์ง Presenter๋ฅผ ํตํด์ ์ํ๋ ๋ณํ๋ฅผ ์๋ ค์ค ์ ์์ต๋๋ค.
์ด๋ ๊ฒ View์ ๋น์ง๋์ค ๋ก์ง์ด ์์ ํ ๋ถ๋ฆฌ๊ฐ ๋์ด ํ ์คํธ๊ฐ ์ฉ์ดํด์ง๋ค๋ ์ฅ์ ์ด ์์ต๋๋ค.
๋ทฐ๊ฐ ์ ๋ฐ์ดํธ ๋๋ ๊ณผ์ ์ ๊ฐ๋จํ ์ค๋ช ํ๋ฉด
1. View์ ์ฌ์ฉ์์ ์ธํฐ๋์ ์ด ๋ค์ด์จ๋ค.
2. View๋ Presenter์ ์ก์ ์ด ๋ค์ด์๋ค๊ณ ์ ๋ฌํ๋ค.
3. Presenter๋ View ์ก์ ๋๋ก Model์ ๊ตฌ์ฑํ๋ค. (๋ณดํต Service๋ฅผ ๊ฐ์ง๊ณ ์๊ณ ์์ฒญํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค.)
4. Update๋ Presenter์ ๋ฐ์ดํฐ๋ฅผ View์ ์ ๋ฐ์ดํธ ํฉ๋๋ค.
์ด๋ฌํ MVP์ ๋จ์ ์ View์ Model์ ๋ถ๋ฆฌ์์ผ MVC๋ Apple์ MVC์์ ํ๊ธฐ ํ๋ค์๋ ํ ์คํธ๊ฐ ์ฉ์ดํด์ก์ง๋ง
View์ Presenter์ ์์กด๊ด๊ณ๊ฐ ๊ฐํด์ง๊ณ Controller ๋์ Presenter๊ฐ ๋ณต์กํด์ง๋ ๋ฌธ์ ๋ ์ฌ์ ํ ๋จ์์์ต๋๋ค.
Model
Character์ ์ด๋ฏธ์ง,์ฌ์ง,์ฑ๋ณ,๋๋ผ๋ฅผ ๊ฐ์ง๊ณ ์๋ ๋ชจ๋ธ์ ๋๋ค.
import UIKit
struct MVP_Character {
//ํ๋ฉด์ ๊ตฌ์ฑํ ๋ฐ์ดํฐ
let image:UIImage
let name:String
let gender:String
let country:String
//์ด๊ธฐํ
init(name:String,image:UIImage,gender:String,country:String) {
self.name = name
self.image = image
self.gender = gender
self.country = country
}
}
Protocol
Presenter์ ํ์ํ ํจ์๋ ๋ณ์ ๋ฑ์ ์ ์ํฉ๋๋ค.
protocol MVP_CharacterPresenterProtocol {
var character:MVP_Character? { get set }
func decrementIndex()
func incrementIndex()
}
Presenter
Protocol์์ ์ ์ํ ํจ์์ ๋ณ์๋ค์ ๊ตฌํํฉ๋๋ค.
๋ณดํต Service๋ฅผ ๊ฐ์ง๊ณ ์๊ณ View์์ ๋ฐ์ ์ธํฐ๋์ ๋๋ก ์์ฒญํ์ฌ Presenter์ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํฉ๋๋ค.
(๊ฐ๋จํ ์์๋ฅผ ์ํด ๋ฏธ๋ฆฌ ๋ชจ๋ธ๋ค์ ๊ฐ์ง๊ณ ์๋ characters๋ฅผ ๋ง๋ค์ด๋์์ต๋๋ค.)
View์ ํ์ํ ๋ฐ์ดํฐ character๋ฅผ ๊ฐ์ง๊ณ ์๊ณ View์์ ์ผ์ด๋๋ ์ก์ ์ ๋ฐ๋ผ ๋ฐ์ดํฐ๊ฐ ๋ฐ๋๊ฒ๋ ๊ตฌํํด๋์ต๋๋ค.
//์ ์ฒด ๋ชจ๋ธ ๋ฐ์ดํฐ
let characters:[MVP_Character] = [
MVP_Character(name:"์คํฐ์ง๋ฐฅ",image: #imageLiteral(resourceName: "แแ
ณแแ
ฉแซแแ
ตแแ
กแธ"), gender: "๋จ์", country:"๋ฏธ๊ตญ"),
MVP_Character(name:"๋ฑ์ด",image: #imageLiteral(resourceName: "แแ
ฎแผแแ
ต"), gender: "๋จ์", country:"๋ฏธ๊ตญ"),
MVP_Character(name:"์ง์ง์ด",image: #imageLiteral(resourceName: "แแ
ตแผแแ
ตแผแแ
ต"), gender: "๋จ์", country:"๋ฏธ๊ตญ"),
MVP_Character(name:"ํ๋ญํฌํค",image: #imageLiteral(resourceName: "แแ
ณแฏแ
แ
กแผแแ
ณแแ
ฉแซ"), gender: "๋จ์", country:"๋ฏธ๊ตญ"),
MVP_Character(name:"ํํ์ฌ์ฌ",image: #imageLiteral(resourceName: "แแ
ฉแผแแ
ฉแผแแ
ฎแแ
ตแซ"), gender: "์ฌ์", country:"๋ฏธ๊ตญ")
]
class MVP_CharacterPresenter:MVP_CharacterPresenterProtocol {
var character: MVP_Character?
var index:Int = 0
func incrementIndex() {
//์ด์ ์ธ๋ฑ์ค๋ก ๋ณ๊ฒฝ
if index > 0 {
index -= 1
}
character = characters[index]
}
func decrementIndex() {
//๋ค์ ์ธ๋ฑ์ค๋ก ๋ณ๊ฒฝ
if index < 4 {
index += 1
}
character = characters[index]
}
}
View
์๋์ ๊ฐ์ด ์บ๋ฆญํฐ์ ์ด๋ฏธ์ง,์ฑ๋ณ,์ด๋ฆ,๋๋ผ๋ฅผ ํ์ํ๊ณ ๋ค์ ์บ๋ฆญํฐ๋ก ๋์ด๊ฐ๋ Next๋ฒํผ๊ณผ
์ด์ ์บ๋ฆญํฐ๋ก ๋์๊ฐ๋ Previous ๋ฒํผ์ด ์์ต๋๋ค.
View๋ ์๋์ ๊ฐ์ด Presenter๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณค Button์ ์ก์ ์ Presenter์์ ๊ตฌํํด๋์ ๊ธฐ๋ฅ์ ๋ง๊ฒ ์ ๋ฌํฉ๋๋ค.
์ก์ ์ ๋ฐ๋ผ ๋ฐ๋ Presenter character์ ๋ฐ์ดํฐ์ ๋ง๊ฒ View๋ฅผ ์ ๋ฐ์ดํธ ํฉ๋๋ค/
import UIKit
class MVP_CharacterViewController: UIViewController {
//MARK:- IBOutlets
@IBOutlet weak var country: UILabel!
@IBOutlet weak var name: UILabel!
@IBOutlet weak var gender: UILabel!
@IBOutlet weak var image: UIImageView!
@IBOutlet weak var nextButton: UIButton!
@IBOutlet weak var previousButton: UIButton!
//MARK:- Properties
let presenter:MVP_CharacterPresenterProtocol = MVP_CharacterPresenter()
//๋ทฐ ์
๋ฐ์ดํธ
private func updateView() {
//์๋ก์ด ๋ชจ๋ธ์ ๋ง๊ฒ ๋ทฐ ๋ณ๊ฒฝ
self.image.image = presenter.character?.image
self.name.text = presenter.character?.name
self.gender.text = presenter.character?.gender
self.country.text = presenter.character?.country
}
@IBAction func tapPreviousButton(_ sender: Any) {
presenter.decrementIndex()
updateView()
}
@IBAction func tapNextButton(_ sender: Any) {
presenter.incrementIndex()
updateView()
}
}
์คํํ๋ฉด
Test
View์ ๋น์ง๋์ค ๋ก์ง์ด ๋ถ๋ฆฌ๊ฐ ๋์์ผ๋ฏ๋ก Test๊ฐ ์ฉ์ดํ๋ค๊ณ ์์ ๋ง์๋๋ ธ์ต๋๋ค.
๋ํ Protocol๋ก Presenter์ ๊ธฐ๋ฅ์ ์ ์ํ๋ ๊ฒ๋ ํ ์คํธ๋ฅผ ํ๊ธฐ ์ํด์์๋๋ฐ์.
์๋์ ๊ฐ์ด Mock ๋ฒ์ ์ Presenter๋ฅผ Protocol์ ์ฑํํด ๋ง๋ค ์ ์์ต๋๋ค.
import Foundation
@testable import Architecture_Example
class Mock_CharacterPresenter:MVP_CharacterPresenterProtocol {
var character: MVP_Character?
var index:Int = 3
func incrementIndex() {
//๋ค์ ์ธ๋ฑ์ค๋ก ๋ณ๊ฒฝ
if index < 4 {
index += 1
}
}
func decrementIndex() {
//์ด์ ์ธ๋ฑ์ค๋ก ๋ณ๊ฒฝ
if index > 0 {
index -= 1
}
}
}
์์ Mock Presenter๋ฅผ ์ด์ฉํด์ ํ๋ ์ ํฐ์ ํ ์คํธ๋ฅผ ์๋์ ๊ฐ์ด ํ ์ ์์ต๋๋ค.
import XCTest
@testable import Architecture_Example
class CharacterPresenterTests: XCTestCase {
var sut:Mock_CharacterPresenter!
override func setUpWithError() throws {
sut = Mock_CharacterPresenter()
}
override func tearDownWithError() throws {
sut = nil
}
func testSignupPresenter_WhenTapNextButton_ShouldIndexIncrement1() throws {
let index = sut.index
sut.incrementIndex()
XCTAssertEqual(sut.index, index+1)
}
func testSignupPresenter_WhenIndexGreaterThanZero_ShouldIndexDecrement1() throws {
let index = sut.index
sut.decrementIndex()
XCTAssertEqual(sut.index, index-1)
}
}
'๐ iOS > Architecture' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[RIBs] Interactor๋ก ๋น๋์ง์ค ๋ก์ง ์ฒ๋ฆฌํด๋ณด๊ธฐ (feat. ์ด๊ธฐ์ธํ ) (0) | 2021.08.18 |
---|---|
[Design Pattern] RIBs๋?(feat. Uber) (0) | 2021.08.18 |
[Design Pattern] ReactorKit์ด๋? (0) | 2021.06.17 |
[Design Pattern] Apple์ MVC ํจํด์ด๋? (0) | 2021.06.03 |
[Design Pattern] MVCํจํด์ด๋? (Model-View-Controller) (0) | 2021.06.03 |
๋๊ธ