-
λͺ¨λ°μΌ μ±μ νκ²½
-
μν€ν μ²μ μ΄μ
-
μ± μν€ν μ²μ ν΅μ¬ μμΉ
-
1. κ΄μ¬μ¬ λΆλ¦¬ (Separation of Concerns)
-
2. λ°μ΄ν° λͺ¨λΈμμ UI λμΆ
-
3. λ¨μΌ μμ€ μ μ₯μ (Single Source of Truth, SSOT)
-
4. λ¨λ°©ν₯ λ°μ΄ν° νλ¦ (Unidirectional Data Flow, UDF)
-
κΆμ₯ μ± μν€ν μ²
-
1. UI λ μ΄μ΄ (Presentation Layer)
-
2. λ°μ΄ν° λ μ΄μ΄ (Data Layer)
-
3. λλ©μΈ λ μ΄μ΄ (Domain Layer, μ νμ¬ν)
-
κ²°λ‘
λͺ¨λ°μΌ μ ν리μΌμ΄μ μ κ°λ°ν λ μ¬λ°λ₯Έ μν€ν μ²λ₯Ό μ μ©νλ©΄ μ μ§λ³΄μμ±κ³Ό νμ₯μ±μ λμ΄κ³ , νμ κ° νμ μ μννκ² νλ©°, μ¬μ©μ κ²½νμ ν₯μμν¬ μ μλ€. μ΄ κΈμμλ μλλ‘μ΄λ μ± μν€ν μ²μ ν΅μ¬ μμΉκ³Ό κΆμ₯μ¬νμ λ€λ£¬λ€.
λͺ¨λ°μΌ μ±μ νκ²½
μλλ‘μ΄λ μ±μ Activity, Fragment, Service, Content Provider, Broadcast Receiver λ± μ¬λ¬ κ΅¬μ± μμλ‘ μ΄λ£¨μ΄μ Έ μμΌλ©°, μ΄μ체μ κ° μ΄λ€μ κ΄λ¦¬νλ€.
νμ§λ§ λͺ¨λ°μΌ νκ²½μ 리μμ€κ° μ νμ μ΄λ©°, μ΄μ체μ κ° νμμ λ°λΌ μ±μ νλ‘μΈμ€λ₯Ό μ’ λ£ν μλ μλ€. λ°λΌμ μ±μ μ€κ³ν λλ κ΅¬μ± μμκ° κ°λ³μ μΌλ‘ μ€νλ μ μλλ‘ νκ³ , μλ‘ μ’ μλμ§ μλλ‘ ν΄μΌ νλ€.
μν€ν μ²μ μ΄μ
- μ μ§λ³΄μμ± μ¦κ° β μ½λμ κ΅¬μ‘°κ° λͺ ννμ¬ μμ μ΄ μ©μ΄
- νμ₯μ± ν보 β μ¬λ¬ κ°λ°μκ° λμμ μμ κ°λ₯
- ν μ€νΈ μ©μ΄μ± β κ° λͺ¨λμ λ 립μ μΌλ‘ ν μ€νΈ κ°λ₯
- λ²κ·Έ λΆμ μ©μ΄ β λ°μ΄ν° νλ¦μ΄ λͺ ννμ¬ λλ²κΉ μ΄ μ¬μ
- μμ μ±κ³Ό μ±λ₯ ν₯μ β μ¬μ©μ κ²½νμ΄ κ°μ λ¨
- μν€ν μ²λ₯Ό μ²μ μ μ©νλ λ° μκ°μ΄ νμνμ§λ§, μ₯κΈ°μ μΌλ‘ 보면 κ°λ° μμ°μ±μ λμ΄κ³ μ±μ νμ§μ κ°μ νλ λ° ν° λμμ΄ λλ€.
μ± μν€ν μ²μ ν΅μ¬ μμΉ
1. κ΄μ¬μ¬ λΆλ¦¬ (Separation of Concerns)
- μ±μ κ° κ΅¬μ± μμλ μμ μ μν μλ§ μ§μ€ν΄μΌ νλ€.
- μλ₯Ό λ€μ΄ Activityμ Fragmentλ UI κ΄λ ¨ λ‘μ§λ§ λ΄λΉν΄μΌ νλ©°, λΉμ¦λμ€ λ‘μ§κ³Ό λ°μ΄ν° μ²λ¦¬λ λ³λμ ν΄λμ€λ‘ λΆλ¦¬νλ κ²μ΄ μ’λ€.
- λμ μμ
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val items = fetchData() // β λ€νΈμν¬ νΈμΆμ μ§μ μν
displayData(items)
}
private fun fetchData(): List<String> {
// API μμ² μν
}
private fun displayData(items: List<String>) {
// λ°μ΄ν°λ₯Ό UIμ νμ
}
}
π μ μ½λλ Activityκ° λ°μ΄ν° μμ²κ³Ό UI μ²λ¦¬λ₯Ό λͺ¨λ λ΄λΉνκ³ μμ΄ μ μ§λ³΄μνκΈ° μ΄λ ΅λ€.
- μ’μ μμ
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.items.observe(this) { items ->
displayData(items)
}
}
private fun displayData(items: List<String>) {
// λ°μ΄ν°λ₯Ό UIμ νμ
}
}
π Activityλ UIλ§ λ΄λΉνκ³ , λ°μ΄ν° μ²λ¦¬λ ViewModelμμ μννλλ‘ κ°μ λμλ€.
2. λ°μ΄ν° λͺ¨λΈμμ UI λμΆ
- UIλ λ°μ΄ν° λͺ¨λΈμ κΈ°λ°μΌλ‘ μμ±λμ΄μΌ νλ©°, λ°μ΄ν° λͺ¨λΈμ UIμ λ 립μ μΌλ‘ μ μ§λμ΄μΌ νλ€.
- μ¦, λ°μ΄ν°κ° λ³κ²½λλ©΄ UIκ° μλμΌλ‘ κ°±μ λλ ꡬ쑰λ₯Ό λ§λλ κ²μ΄ μ€μνλ€.
- LiveData, StateFlow κ°μ λ°μν λ°μ΄ν° μ¬μ© κΆμ₯ β
class MainViewModel : ViewModel() {
private val _items = MutableLiveData<List<String>>()
val items: LiveData<List<String>> get() = _items
fun fetchData() {
// λ€νΈμν¬ μμ² ν λ°μ΄ν° κ°±μ
_items.value = listOf("Item1", "Item2", "Item3")
}
}
π UIλ ViewModelμ LiveDataλ₯Ό κ΄μ°°νμ¬ μλμΌλ‘ μ λ°μ΄νΈλλ€.
3. λ¨μΌ μμ€ μ μ₯μ (Single Source of Truth, SSOT)
- νλμ λ°μ΄ν° μ νμ λν΄μλ λ¨ νλμ λ°μ΄ν° μμ€λ§ μ‘΄μ¬ν΄μΌ νλ€.
- μλ₯Ό λ€μ΄, λ€νΈμν¬μμ λ°μ΄ν°λ₯Ό κ°μ Έμ€κ±°λ λ‘컬 DBμμ λ°μ΄ν°λ₯Ό μ½λ κ²½μ° λͺ¨λ λ°μ΄ν° μ²λ¦¬λ Repositoryμμ μνν΄μΌ νλ€.
- Repository ν¨ν΄ μ μ© β
class UserRepository(private val apiService: ApiService, private val userDao: UserDao) {
suspend fun getUserData(): User {
val user = apiService.getUser()
userDao.insert(user) // λ‘컬 DBμ μ μ₯
return user
}
}
π UserRepositoryκ° λ¨μΌ λ°μ΄ν° μμ€ μν μ νλ©°, λ€νΈμν¬ λλ λ‘컬 λ°μ΄ν°λ₯Ό μΌκ΄λκ² κ΄λ¦¬νλ€.
4. λ¨λ°©ν₯ λ°μ΄ν° νλ¦ (Unidirectional Data Flow, UDF)
- λ°μ΄ν°λ νμ ν λ°©ν₯μΌλ‘ νλ₯΄λλ‘ μ€κ³ν΄μΌ νλ€.
- μ¦, View β ViewModel β Repository β Data Source μμλλ‘ λ°μ΄ν°κ° νλ₯Έλ€.
- λ°μ΄ν° λ³κ²½ μ΄λ²€νΈλ λ°λ λ°©ν₯μΌλ‘ μ λ¬λλ€.
- UDF μ μ© μμ β
μ¬μ©μ μ‘μ
(λ²νΌ ν΄λ¦) β ViewModelμ μ΄λ²€νΈ μ λ¬ β Repositoryμμ λ°μ΄ν° μμ² β UI μ
λ°μ΄νΈ
// ViewModel
fun onButtonClicked() {
viewModelScope.launch {
val userData = repository.getUserData()
_user.postValue(userData)
}
}
π λ°μ΄ν° νλ¦μ λͺ ννκ² λΆλ¦¬νμ¬ μ μ§λ³΄μκ° μ¬μμ§λ€.
κΆμ₯ μ± μν€ν μ²
ꡬκΈμ΄ κΆμ₯νλ μ± μν€ν μ²λ UI λ μ΄μ΄, λ°μ΄ν° λ μ΄μ΄, λλ©μΈ λ μ΄μ΄λ‘ ꡬμ±λλ€.

1. UI λ μ΄μ΄ (Presentation Layer)
- View (Activity, Fragment, Compose)
- ViewModel (UI μν λ° λ‘μ§ κ΄λ¦¬)
2. λ°μ΄ν° λ μ΄μ΄ (Data Layer)
- Repository (λ°μ΄ν° μμ€ κ΄λ¦¬)
- Data Source (λ‘컬 DB, λ€νΈμν¬ API λ±)
3. λλ©μΈ λ μ΄μ΄ (Domain Layer, μ νμ¬ν)
- UseCase (λΉμ¦λμ€ λ‘μ§ μ²λ¦¬ λ° λ°μ΄ν° λ³ν)
κΆμ₯ μν€ν μ² λ€μ΄μ΄κ·Έλ¨ β
[ UI Layer ]
β View (Activity, Fragment, Compose)
β ViewModel (LiveData, StateFlow)
[ Domain Layer ] (μ νμ )
β UseCase (λΉμ¦λμ€ λ‘μ§)
[ Data Layer ]
β Repository (λ°μ΄ν° κ΄λ¦¬)
β Data Source (API, DB)
π λ μ΄μ΄λ₯Ό λΆλ¦¬νλ©΄ μ½λκ° λͺ νν΄μ§κ³ μ μ§λ³΄μκ° μ¬μμ§λ€.
κ²°λ‘
μλλ‘μ΄λ μ±μ κ°λ°ν λλ κ΄μ¬μ¬ λΆλ¦¬, SSOT, λ¨λ°©ν₯ λ°μ΄ν° νλ¦ κ°μ μμΉμ μ μ©νμ¬ μ μ§λ³΄μμ±κ³Ό νμ₯μ±μ λμ¬μΌ νλ€.
β ν΅μ¬ μμ½
- UIμ λ°μ΄ν° μ²λ¦¬λ₯Ό λΆλ¦¬νμ¬ κ΄λ¦¬νκΈ° μ½λλ‘
- ViewModelκ³Ό LiveDataλ₯Ό νμ©νμ¬ λ°μν UI ꡬν
- Repository ν¨ν΄μ μ μ©νμ¬ λ¨μΌ λ°μ΄ν° μμ€ μ μ§
- UDF ν¨ν΄μ μ¬μ©νμ¬ λ°μ΄ν° νλ¦μ ν λ°©ν₯μΌλ‘ μ μ§
π‘ μ²μμλ 볡μ‘ν΄ λ³΄μΌ μ μμ§λ§, μ₯κΈ°μ μΌλ‘ 보면 κ°λ° ν¨μ¨μ±κ³Ό μ± νμ§μ΄ ν¬κ² ν₯μλλ€.
'π€ μλλ‘μ΄λ > μν€ν μ²' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
λͺ¨λ°μΌ μ ν리μΌμ΄μ μ κ°λ°ν λ μ¬λ°λ₯Έ μν€ν μ²λ₯Ό μ μ©νλ©΄ μ μ§λ³΄μμ±κ³Ό νμ₯μ±μ λμ΄κ³ , νμ κ° νμ μ μννκ² νλ©°, μ¬μ©μ κ²½νμ ν₯μμν¬ μ μλ€. μ΄ κΈμμλ μλλ‘μ΄λ μ± μν€ν μ²μ ν΅μ¬ μμΉκ³Ό κΆμ₯μ¬νμ λ€λ£¬λ€.
λͺ¨λ°μΌ μ±μ νκ²½
μλλ‘μ΄λ μ±μ Activity, Fragment, Service, Content Provider, Broadcast Receiver λ± μ¬λ¬ κ΅¬μ± μμλ‘ μ΄λ£¨μ΄μ Έ μμΌλ©°, μ΄μ체μ κ° μ΄λ€μ κ΄λ¦¬νλ€.
νμ§λ§ λͺ¨λ°μΌ νκ²½μ 리μμ€κ° μ νμ μ΄λ©°, μ΄μ체μ κ° νμμ λ°λΌ μ±μ νλ‘μΈμ€λ₯Ό μ’ λ£ν μλ μλ€. λ°λΌμ μ±μ μ€κ³ν λλ κ΅¬μ± μμκ° κ°λ³μ μΌλ‘ μ€νλ μ μλλ‘ νκ³ , μλ‘ μ’ μλμ§ μλλ‘ ν΄μΌ νλ€.
μν€ν μ²μ μ΄μ
- μ μ§λ³΄μμ± μ¦κ° β μ½λμ κ΅¬μ‘°κ° λͺ ννμ¬ μμ μ΄ μ©μ΄
- νμ₯μ± ν보 β μ¬λ¬ κ°λ°μκ° λμμ μμ κ°λ₯
- ν μ€νΈ μ©μ΄μ± β κ° λͺ¨λμ λ 립μ μΌλ‘ ν μ€νΈ κ°λ₯
- λ²κ·Έ λΆμ μ©μ΄ β λ°μ΄ν° νλ¦μ΄ λͺ ννμ¬ λλ²κΉ μ΄ μ¬μ
- μμ μ±κ³Ό μ±λ₯ ν₯μ β μ¬μ©μ κ²½νμ΄ κ°μ λ¨
- μν€ν μ²λ₯Ό μ²μ μ μ©νλ λ° μκ°μ΄ νμνμ§λ§, μ₯κΈ°μ μΌλ‘ 보면 κ°λ° μμ°μ±μ λμ΄κ³ μ±μ νμ§μ κ°μ νλ λ° ν° λμμ΄ λλ€.
μ± μν€ν μ²μ ν΅μ¬ μμΉ
1. κ΄μ¬μ¬ λΆλ¦¬ (Separation of Concerns)
- μ±μ κ° κ΅¬μ± μμλ μμ μ μν μλ§ μ§μ€ν΄μΌ νλ€.
- μλ₯Ό λ€μ΄ Activityμ Fragmentλ UI κ΄λ ¨ λ‘μ§λ§ λ΄λΉν΄μΌ νλ©°, λΉμ¦λμ€ λ‘μ§κ³Ό λ°μ΄ν° μ²λ¦¬λ λ³λμ ν΄λμ€λ‘ λΆλ¦¬νλ κ²μ΄ μ’λ€.
- λμ μμ
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val items = fetchData() // β λ€νΈμν¬ νΈμΆμ μ§μ μν
displayData(items)
}
private fun fetchData(): List<String> {
// API μμ² μν
}
private fun displayData(items: List<String>) {
// λ°μ΄ν°λ₯Ό UIμ νμ
}
}
π μ μ½λλ Activityκ° λ°μ΄ν° μμ²κ³Ό UI μ²λ¦¬λ₯Ό λͺ¨λ λ΄λΉνκ³ μμ΄ μ μ§λ³΄μνκΈ° μ΄λ ΅λ€.
- μ’μ μμ
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.items.observe(this) { items ->
displayData(items)
}
}
private fun displayData(items: List<String>) {
// λ°μ΄ν°λ₯Ό UIμ νμ
}
}
π Activityλ UIλ§ λ΄λΉνκ³ , λ°μ΄ν° μ²λ¦¬λ ViewModelμμ μννλλ‘ κ°μ λμλ€.
2. λ°μ΄ν° λͺ¨λΈμμ UI λμΆ
- UIλ λ°μ΄ν° λͺ¨λΈμ κΈ°λ°μΌλ‘ μμ±λμ΄μΌ νλ©°, λ°μ΄ν° λͺ¨λΈμ UIμ λ 립μ μΌλ‘ μ μ§λμ΄μΌ νλ€.
- μ¦, λ°μ΄ν°κ° λ³κ²½λλ©΄ UIκ° μλμΌλ‘ κ°±μ λλ ꡬ쑰λ₯Ό λ§λλ κ²μ΄ μ€μνλ€.
- LiveData, StateFlow κ°μ λ°μν λ°μ΄ν° μ¬μ© κΆμ₯ β
class MainViewModel : ViewModel() {
private val _items = MutableLiveData<List<String>>()
val items: LiveData<List<String>> get() = _items
fun fetchData() {
// λ€νΈμν¬ μμ² ν λ°μ΄ν° κ°±μ
_items.value = listOf("Item1", "Item2", "Item3")
}
}
π UIλ ViewModelμ LiveDataλ₯Ό κ΄μ°°νμ¬ μλμΌλ‘ μ λ°μ΄νΈλλ€.
3. λ¨μΌ μμ€ μ μ₯μ (Single Source of Truth, SSOT)
- νλμ λ°μ΄ν° μ νμ λν΄μλ λ¨ νλμ λ°μ΄ν° μμ€λ§ μ‘΄μ¬ν΄μΌ νλ€.
- μλ₯Ό λ€μ΄, λ€νΈμν¬μμ λ°μ΄ν°λ₯Ό κ°μ Έμ€κ±°λ λ‘컬 DBμμ λ°μ΄ν°λ₯Ό μ½λ κ²½μ° λͺ¨λ λ°μ΄ν° μ²λ¦¬λ Repositoryμμ μνν΄μΌ νλ€.
- Repository ν¨ν΄ μ μ© β
class UserRepository(private val apiService: ApiService, private val userDao: UserDao) {
suspend fun getUserData(): User {
val user = apiService.getUser()
userDao.insert(user) // λ‘컬 DBμ μ μ₯
return user
}
}
π UserRepositoryκ° λ¨μΌ λ°μ΄ν° μμ€ μν μ νλ©°, λ€νΈμν¬ λλ λ‘컬 λ°μ΄ν°λ₯Ό μΌκ΄λκ² κ΄λ¦¬νλ€.
4. λ¨λ°©ν₯ λ°μ΄ν° νλ¦ (Unidirectional Data Flow, UDF)
- λ°μ΄ν°λ νμ ν λ°©ν₯μΌλ‘ νλ₯΄λλ‘ μ€κ³ν΄μΌ νλ€.
- μ¦, View β ViewModel β Repository β Data Source μμλλ‘ λ°μ΄ν°κ° νλ₯Έλ€.
- λ°μ΄ν° λ³κ²½ μ΄λ²€νΈλ λ°λ λ°©ν₯μΌλ‘ μ λ¬λλ€.
- UDF μ μ© μμ β
μ¬μ©μ μ‘μ
(λ²νΌ ν΄λ¦) β ViewModelμ μ΄λ²€νΈ μ λ¬ β Repositoryμμ λ°μ΄ν° μμ² β UI μ
λ°μ΄νΈ
// ViewModel
fun onButtonClicked() {
viewModelScope.launch {
val userData = repository.getUserData()
_user.postValue(userData)
}
}
π λ°μ΄ν° νλ¦μ λͺ ννκ² λΆλ¦¬νμ¬ μ μ§λ³΄μκ° μ¬μμ§λ€.
κΆμ₯ μ± μν€ν μ²
ꡬκΈμ΄ κΆμ₯νλ μ± μν€ν μ²λ UI λ μ΄μ΄, λ°μ΄ν° λ μ΄μ΄, λλ©μΈ λ μ΄μ΄λ‘ ꡬμ±λλ€.

1. UI λ μ΄μ΄ (Presentation Layer)
- View (Activity, Fragment, Compose)
- ViewModel (UI μν λ° λ‘μ§ κ΄λ¦¬)
2. λ°μ΄ν° λ μ΄μ΄ (Data Layer)
- Repository (λ°μ΄ν° μμ€ κ΄λ¦¬)
- Data Source (λ‘컬 DB, λ€νΈμν¬ API λ±)
3. λλ©μΈ λ μ΄μ΄ (Domain Layer, μ νμ¬ν)
- UseCase (λΉμ¦λμ€ λ‘μ§ μ²λ¦¬ λ° λ°μ΄ν° λ³ν)
κΆμ₯ μν€ν μ² λ€μ΄μ΄κ·Έλ¨ β
[ UI Layer ]
β View (Activity, Fragment, Compose)
β ViewModel (LiveData, StateFlow)
[ Domain Layer ] (μ νμ )
β UseCase (λΉμ¦λμ€ λ‘μ§)
[ Data Layer ]
β Repository (λ°μ΄ν° κ΄λ¦¬)
β Data Source (API, DB)
π λ μ΄μ΄λ₯Ό λΆλ¦¬νλ©΄ μ½λκ° λͺ νν΄μ§κ³ μ μ§λ³΄μκ° μ¬μμ§λ€.
κ²°λ‘
μλλ‘μ΄λ μ±μ κ°λ°ν λλ κ΄μ¬μ¬ λΆλ¦¬, SSOT, λ¨λ°©ν₯ λ°μ΄ν° νλ¦ κ°μ μμΉμ μ μ©νμ¬ μ μ§λ³΄μμ±κ³Ό νμ₯μ±μ λμ¬μΌ νλ€.
β ν΅μ¬ μμ½
- UIμ λ°μ΄ν° μ²λ¦¬λ₯Ό λΆλ¦¬νμ¬ κ΄λ¦¬νκΈ° μ½λλ‘
- ViewModelκ³Ό LiveDataλ₯Ό νμ©νμ¬ λ°μν UI ꡬν
- Repository ν¨ν΄μ μ μ©νμ¬ λ¨μΌ λ°μ΄ν° μμ€ μ μ§
- UDF ν¨ν΄μ μ¬μ©νμ¬ λ°μ΄ν° νλ¦μ ν λ°©ν₯μΌλ‘ μ μ§
π‘ μ²μμλ 볡μ‘ν΄ λ³΄μΌ μ μμ§λ§, μ₯κΈ°μ μΌλ‘ 보면 κ°λ° ν¨μ¨μ±κ³Ό μ± νμ§μ΄ ν¬κ² ν₯μλλ€.