๐ก ์ฐ์ํํ ํฌ์ฝ์ค ๊ณผ์ ์ ์งํํ๋ฉด์ MVC ๋์์ธํจํด์ MVP๋ก ๋ง์ด๊ทธ๋ ์ด์ ์ ์งํํ ์ด์ ์ ๊ณผ์ ์ ๋ํ์ฌ ๊ธฐ๋กํ์์ต๋๋ค!
๊ฐ์
- ์ด์ ์ MVC ํจํด์ ๋ํ์ฌ ํ์ตํ์์ต๋๋ค.
- ํ์ง๋ง MVC ํจํด์ผ๋ก ๊ตฌํ ํ ํ ์คํธ์ ์์กด์ฑ ๊ด๋ จํด์ ๋ฌธ์ ์ ์ด ์๋ค๋ ๊ฒ์ ์๊ฒ ๋์๊ณ , ์ด๋ฅผ ๋ณด์ํ MVP ํจํด์ ๋ํด์ ํ์ตํ์์ต๋๋ค.
- MVCํจํด์ MVP ํจํด์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ์ ๋ ์ป๋ ์ด์ ๊ณผ ๊ณผ์ ์ ์ ๋ฆฌํด๋ณด๋ ค ํฉ๋๋ค.
MVC
- ์ฐ์ํํ ํฌ์ฝ์ค ๊ณผ์ ์ด๊ธฐ์ MVC ํจํด์ ์ฃผ๋ก ํ์ฉํ์์ต๋๋ค.
- MVC ํจํด์์ ์ปจํธ๋กค๋ฌ๋ ๋ทฐ๋ก๋ถํฐ ์ ์ ์ ์ด๋ฒคํธ๋ก ๋ชจ๋ธ์ ๋ณ๊ฒฝํ์ฌ ๋ค์ ์ปจํธ๋กค๋ฌ์ ์ ๋ฌํด ๋ทฐ๋ฅผ ์ ๋ฐ์ดํธ ํฉ๋๋ค.
- ์ด๋ฌํ ๊ณผ์ ์์ ๋ชจ๋ธ๊ณผ ๋ทฐ๋ฅผ ๋ถ๋ฆฌํ๊ธด ํ์์ง๋ง, ์ปจํธ๋กค๋ฌ๊ฐ ์ง๋์น๊ฒ ๋น๋ํด์ง๋ ๊ณผ์ ์ด ๋ฐ์ํ์์ต๋๋ค.
- ๋ํ ์ด๊ธฐ์๋ ์ฃผ๋ก ์ฝ์์ ํ์ฉํ I/O ํ๋ก๊ทธ๋จ์ ๊ตฌํํ์์ง๋ง, Activity๋ฅผ ํ์ฉํ๋ ์๋๋ก์ด๋ ํ๋ ์์ํฌ๋ก ๋์ด๊ฐ๋ฉด์ ๋ฌธ์ ๊ฐ ์๊ธฐ๊ฒ ๋์์ต๋๋ค.
MVC with Android
View์ Controller์ ๊ฐํ ๊ฒฐํฉ
- ์๋๋ก์ด๋์์๋ xml ํ์ผ์ ํตํด์ View๋ฅผ ๋ณด์ฌ์ฃผ๊ณ , ํด๋น View์ ์ด๋ฒคํธ๋ ๋ณํ๋ฅผ ๋ฐ์ ๋์์ ์ํํ๋ ์กํฐ๋นํฐ๊ฐ ์กด์ฌํฉ๋๋ค.
- ์ผ๋ฐ์ ์ผ๋ก xml๊ณผ activity๋ 1:1 ๊ด๊ณ๋ก ๊ตฌ์ฑ๋๋๋ฐ, ์ด๋ก์ธํด Controller๊ฐ activity์ ๋ก์ง๊ณผ ๋์ผํด์ง๊ณ Activity์์๋ ๋ทฐ ๋ก์ง๊ณผ Controller์ ์ญํ ์ ๋์์ ๋ด๋นํ๊ฒ ๋ฉ๋๋ค.
Activity or Fragment์ ๊ณผ๋ํ ์ฑ ์
- God Object ๋ฌธ์ ๋ฐ์
- Activity, Fragment๊ฐ View์ Controller์ ์ญํ ์ ๋์์ ์ํํ๋ฉด์ ์ฝ๋์ ์์ด ๊ธ๊ฒฉํ๊ฒ ์ฆ๊ฐํ๊ณ ๋ณต์กํด์ง๋๋ค.
- ์ด๋ฅผ Got Object ๋ฌธ์ ๋ผ๊ณ ํ๋ฉฐ, ํ ํด๋์ค์ ๋๋ฌด ๋ง์ ์ฑ ์์ด ์ง์ค๋๋ ํ์์ ์๋ฏธํฉ๋๋ค.
- ์ด๋ก ์ธํด ์์ ๋ณ๊ฒฝ์๋ ํฐ ์ํฅ์ ๋ฏธ์น ์ ์์ผ๋ฉฐ, ๋ฒ๊ทธ ๋ฐ์ ๊ฐ๋ฅ์ฑ๊ณผ ์ฝ๋ ๊ฐ๋ ์ฑ์ด ์ ํ๋ฉ๋๋ค.
์ด๋ ค์ด ํ ์คํธ
- Controller์ View์ ๊ฐํ ๊ฒฐํฉ์ผ๋ก ๋น์ฆ๋์ค ๋ก์ง์ ๋ ๋ฆฝ์ ์ผ๋ก ํ ์คํธํ๊ธฐ ์ด๋ ต์ต๋๋ค.
- Activity๋ Fragment๋ Android ํ๋ ์์ํฌ์ ๊ฐํ๊ฒ ์์กดํ๋ฏ๋ก, ๋จ์ ํ ์คํธ๋ฅผ ์ํด์ ๋ณต์กํ Mocking์ด ํ์ํฉ๋๋ค.
- ํ ์คํธ ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ณ , ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง๊ฐ ๋ฎ์์ง๋ ๋จ์ ์ด ์์ต๋๋ค.
- [ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง] : ์์คํ ์ํํธ์จ์ด์์ ํ ์คํธ๊ฐ ์ผ๋ง๋ ์ถฉ๋ถํ๊ฐ๋ฅผ ๋ํ๋ด๋ ์งํ๋ก, ์ํํ ํ ์คํธ๊ฐ ๋์์ ์ด๋ง๋ ์ปค๋ฒํ๋์ง ๋ํ๋ ๋๋ค.
๋น์ฆ๋์ค ๋ก์ง๊ณผ UI ๋ก์ง์ ํผํฉ
- MVC ํจํด์์ ๋ก์ง์ด ๋ณต์กํด์ง ๊ฒฝ์ฐ ๋น์ฆ๋์ค ๋ก์ง๊ณผ UI ๋ก์ง์ด ํผํฉ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค.
- ์ด๋ก ์ธํด ์ฝ๋์ ๋ชจ๋ํ๊ฐ ๋จ์ด์ง๊ณ , ์ฝ๋ ๋ณ๊ฒฝ์ผ๋ก ์ธํ ๋ฒ๊ทธ ๋ฐ์ ๊ฐ๋ฅ์ฑ์ด ๋์์ง๋๋ค.
MVP๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ๋ ์ด์
ํ ์คํธ ๊ฐ๋ฅ์ฑ ํฅ์
- MVC์์๋ Controller๊ฐ View์ ๊ฐํ๊ฒ ๊ฒฐํฉ๋์ด ์์ด ํ ์คํธ๊ฐ ์ด๋ ต์ง๋ง, MVP๋ Presenter๊ฐ ์ธํฐํ์ด์ค๋ฅผ ํตํด View์ ์ํธ์์ฉํ๊ธฐ ๋๋ฌธ์ ๋จ์ ํ ์คํธ๊ฐ ์ฉ์ดํฉ๋๋ค.
์ ์ฐ์ฑ ์ฆ๊ฐ
- View Presenter์ ๋ช ํํ ๋ถ๋ฆฌ๋ก ์ธํด UI ๋ณ๊ฒฝ ์ Presenter๋ฅผ ์์ ํ ํ์๊ฐ ์์ผ๋ฏ๋ก ๋ ์ ์ฐํ ๊ตฌ์กฐ๋ฅผ ์ ๊ณตํฉ๋๋ค.
์ฝ๋ ์ ์ง๋ณด์์ฑ๊ณผ ์ฌ์ฌ์ฉ์ฑ
- ๊ฐ ์ปดํฌ๋ํธ์ ์ญํ ์ด ๋ ๋ช ํํ๊ฒ ๋ถ๋ฆฌ๋์ด ์์ด ์ ์ง๋ณด์์ ํ์ฅ์ด ์ฉ์ดํฉ๋๋ค.
- Presenter๊ฐ ๋ก์ง์ ๋ด๋นํ๋ฏ๋ก, ๋ค๋ฅธ View์์๋ ๋์ผํ Presenter๋ฅผ ์ฌ์ฉํ ์ ์์ด ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
๋ง์ด๊ทธ๋ ์ด์ ์งํํ๊ธฐ
- ์ค์ ๋ก๋ Contract Interface๋ฅผ ํ์ฉํ์ฌ View ์ธํฐํ์ด์ค์ Presenter ์ธํฐํ์ด์ค๋ฅผ ํ ๊ณณ์์ ๊ด๋ฆฌํ์์ง๋ง ๋ง์ด๊ทธ๋ ์ด์ ํ๋ ๊ณผ์ ์ ์ํด์ ๋ถ๋ฆฌํ์์ต๋๋ค.
View Interface ์์ฑ
- MVC ๊ตฌ์กฐ์ View์์ ์ธํฐํ์ด์ค๋ฅผ ์ถ์ถํ์์ต๋๋ค.
interface MainView {
fun showLoading()
fun hideLoading()
fun showData(data: List<String>)
fun showError(error: String)
}
Presenter ํด๋์ค ์์ฑ
- MVC์ Controller ์ญํ ์ ํ๋ ๋ก์ง์ Presenter๋ก ์ด๋ํ์์ต๋๋ค.
- Presenter๋ View ์ธํฐํ์ด์ค๋ฅผ ์์กด์ฑ์ผ๋ก ๊ฐ์ง๋ฉฐ, View์ Model ๊ฐ์ ์ํธ์์ฉ์ ์ฒ๋ฆฌํฉ๋๋ค.
class MainPresenter(private val view: MainView, private val model: DataModel) {
fun loadData() {
view.showLoading()
val data = model.fetchData()
if (data.isNotEmpty()) {
view.showData(data)
} else {
view.showError("No data found")
}
view.hideLoading()
}
}
๊ธฐ์กด์ View ํด๋์ค์์ Presenter ์ฌ์ฉ
- ๊ธฐ์กด์ Activity๋ Fragment๋ฅผ Presenter์ ์ํธ์์ฉํ๋๋ก ๋ณ๊ฒฝํฉ๋๋ค.
- View ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ์ฌ, MainView์ ํจ์๋ค์ ์ค๋ฒ๋ผ์ด๋ฉ ํ์์ต๋๋ค.
class MainActivity : AppCompatActivity(), MainView {
private lateinit var presenter: MainPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Model ์ด๊ธฐํ
val model = DataModel()
// Presenter ์ด๊ธฐํ
presenter = MainPresenter(this, model)
// ๋ฐ์ดํฐ ๋ก๋
presenter.loadData()
}
override fun showLoading() {
// ๋ก๋ฉ ํ์
}
override fun hideLoading() {
// ๋ก๋ฉ ์จ๊น
}
override fun showData(data: List<String>) {
// ๋ฐ์ดํฐ๋ฅผ UI์ ํ์
}
override fun showError(error: String) {
// ์๋ฌ ๋ฉ์์ง ํ์
}
}
ํ ์คํธ ์ฝ๋ ์์ฑ
- MVP ํจํด์ด ํ ์คํธ ์์ฑ์ ์ฉ์ดํ์ง ํ์ธํ๊ธฐ ์ํด์ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด๋ณด์์ต๋๋ค.
- Mocking ๋ผ์ด๋ธ๋ฌ๋ฆฌ(Mockito)๋ฅผ ํ์ฉํ์ฌ View๋ฅผ ๋ชจ์ ๊ฐ์ฒด๋ก ๋ง๋ค๊ณ , Presenter์ ๋์์ ๊ฒ์ฆํ์์ต๋๋ค.
class MainPresenterTest {
private val mockView = mock(MainView::class.java)
private val mockModel = mock(DataModel::class.java)
private lateinit var presenter: MainPresenter
@Before
fun setup() {
presenter = MainPresenter(mockView, mockModel)
}
@Test
fun testLoadData() {
val dummyData = listOf("Data1", "Data2")
`when`(mockModel.fetchData()).thenReturn(dummyData)
presenter.loadData()
// verify๋ฅผ ํตํด์ ๋์ ํ์ธ
verify(mockView).showLoading()
verify(mockView).showData(dummyData)
verify(mockView).hideLoading()
}
}
๊ฒฐ๋ก
- MVC ํจํด์ MVP ํจํด์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ์์ต๋๋ค.
- ์ด๋ก ์ธํด์ ์๋์ ๊ฐ์ ์ด์ ์ ์ป์ ์ ์์์ต๋๋ค.
์ ์ฐ์ฑ ์ฆ๊ฐ
- MVP๋ก ๋ง์ด๊ทธ๋ ์ด์ ํจ์ผ๋ก์จ View์ Presenter์ ๋ถ๋ฆฌ๊ฐ ๋ช ํํด์ก์ต๋๋ค.
- ์ด๋ก ์ธํด ์ ์ง๋ณด์์ ์ฝ๋ ๊ด๋ฆฌ๊ฐ ์ฌ์์ก์ต๋๋ค.
ํ ์คํธ ๊ฐ๋ฅ์ฑ
- ๊ธฐ์กด์ Controller๋ฅผ ํ ์คํธํ๊ธฐ ์ด๋ ค์ ์ง๋ง, Presenter๋ก ์ ํํ์ฌ View์์ ์์กด์ฑ์ ๊ฐ์์์ผฐ์ต๋๋ค.
- ์ด๋ก ์ธํด์ UI ๋ก์ง์ Presenter์์ ๋ ๋ฆฝ์ ์ผ๋ก ํ ์คํธํ ์ ์์ด์ ๋จ์ ํ ์คํธ ์์ฑ์ด ์ฉ์ดํด์ก์ต๋๋ค.
๊ฐ๋ฐ ์์ฐ์ฑ
- Presenter์ View๊ฐ ๋ ๋ฆฝ์ ์ด๊ธฐ ๋๋ฌธ์ UI ๋ณ๊ฒฝ ์ ๋น์ฆ๋์ค ๋ก์ง์ ๋ณ๊ฒฝ์ด ํ์ ์์ด ๊ฐ๋ฐ ์๋๊ฐ ๋นจ๋ผ์ง๋๋ค.