안드로이드 앱 개발 공부 - MVVM 패턴 익히기
안드로이드 앱 개발 공부 - MVVM 패턴 익히기 — #개발자의도구들 #안드로이드앱개발공부 #MVVM #MVVM패턴 🚨📝 📖 📒✏️💡🔍 AI스...
#개발자의도구들 #안드로이드앱개발공부 #MVVM #MVVM패턴
🚨📝 📖 📒✏️💡🔍
AI스쿨 msa기반 java 백엔드 코스 중에 공부한 내용을 작성하였습니다
\* 현재 Spring x 프로젝트를 진행하고 있습니다. 전체 목차를 보시려면 여기를 눌러주세요.
\* 해당 프로젝트 이전에 공부한 내용을 보시려면 여기를 눌러주세요.
안드로이드 앱 개발 공부 시작
기본적인 앱 아키텍쳐
현재 Spring boot를 사용하여 서버를 제작하고 있습니다. 기존에는 API 테스트를 Postman을 사용하여 진행하였었습니다. 하지만, 안드로이드 앱 개발 공부를 시작하기로 하면서 jetpack compse를 사용하여 프론트 화면을 직접 만들고 네트워크 통신을 직접 해보는 과정까지 진행하기로 하였습니다.
최종 목표는 X에서 제공되는 기능들을 최대한 뽑아내어, 직접 구현을 해보고, 공부한 내용을 최대한 깔끔하게 정리해 두는 것입니다. (언젠가는 다시 보면서 구현할 것이거든요)
이번글에는 안드로이드 앱 설계의 가장 기본이되는 MVVM 구조를 누구나 빠르게 적용할 수 있도록 정리하였습니다.
Index
- Purpose
- MVVM이란?
- VIEWMODEL 사용이유
- MVVM 적용하기
- ApiInterface
- Repository
- VieModel
- View
- Question
- Answer
- Feedback
| 💡 Tips |
|---|
Purpose
MVVM 패턴을 여러번 공부했는데, 막상 적용하려니 헷갈리는 부분이 굉장히 맣았습니다. 특히나 Spring Server의 Controller, Service, Repository 때문에 더 헷갈렸던 것 같습니다 예를들어 MVVM을 적용한 Android의 Repository와 Spring의 MVC의 Repository가 다르다는 점입니다.
이번 글을 통해서 MVVM 패턴이 무엇인지 확실하게 알아보고, 매핑되는 클래스를 제시하여 클린코드의 가이드가 되었으면 좋겠습니다.
MVVM이란?
안드로이드 앱은 Model-View-ViewModel 세 개의 Layer 구조를 사용하고 있습니다. 이름이 거창해 보이지만 사실 MVC아 별 차이가 없습니다.
Spring에서 MVC가 View가 프론트 영역으로 변경되면서, MSC가 된 것 처럼, 안드로이드에서 MVC가 MVVM으로 된거로 해석해도 될 것 같습니다.
🧑💻 VIEW
VIEW레이어는 사용자의 화면에 직접 보여지는 부분을 의미합니다.
🧑💻 MODEL
MODEL은 데이터 저장소와 직접적으로 연계되는 레이어입니다.
안드로이드 내부의 DB와 연계하거나, API 호출이 이뤄지는 부분으로 볼 수도 있습니다.
🧑💻 MODEL
MODEL은 데이터 저장소와 직접적으로 연계되는 레이어입니다.
안드로이드 내부의 DB와 연계하거나, API 호출이 이뤄지는 부분으로 볼 수도 있습니다.
| 🤔 VIEWMODEL을 왜 사용하는지? |
|---|
Viewmodel이 없던 시절을 생각해보면, Viewmodel이 왜 사용하기 편리한지 이해할 수 있습니다.
ViewModel 적용 전
// viewmodel 적용 이전 (jetpack compose)
@Composable
fun CounterScreen() {
var count by remember { mutableStateOf(0) }
Text(text = "Count: $count", style = MaterialTheme.typography.headlineMedium)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { count++ }) {
Text("Increment")
}
}
CounterScreen(View layer)에서 직접적으로 count라는 데이터를 관리합니다. 그럼 아래와 같은 문제점이 발생할 수 있습니다.
- 현재 상황: View layer에 데이터가 함께 관리되는 상황이다.
- 컴포즈 UI: 데이터가 초기화 되면 새롭게 UI가 그려진다.
- 데이터 변화: count가 증가하면 새롭게 UI가 그려지는데 이때, 데이터가 초기화 된다. (⚠️)
ViewModel 적용 후
// viewmodel 적용 이전 (jetpack compose)
class CounterViewModel: ViewModel() {
val count = mutableStateOf(0)
incrementCount() {
count.value += 1
}
}
// viewmodel 적용 이전 (jetpack compose)
@Composable
fun countAppScreen(viewModel: CounterViewModel) {
Text(text = "Count: ${viewModel.count.value}", style = MaterialTheme.typography.headlineMedium)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { viewModel.incrementCount() }) {
Text("Increment")
}
}
- View 레이어와 데이터 관리 레이어를 분리한다.
- 실제 데이터 변화는 ViewModel에서 발생한다
- 데이터 변화시 Screen이 갱신되어도 데이터가 초기화되지 않는다.
마지막으로 정리하자면, ViewModel은 Model에서 불러온 데이터를 각 View에 맞게 적절하게 관리하는 레이어입니다. method를 직접 가지고 있어 데이터를 ViewModel을 통해 관리할 수 잇습니다.
MVVM 적용하기
자, 이론은 이정도면 크게 문제가 없으리라 생각됩니다. 이제는 ViewModel Layer를 직접 적용해 보겠습니다.
| 👉 해당 코드는 의존성 주입 Hilt가 적용되었습니다. Hilt에 대해서는 차후 추가로 정리하겠습니다. |
|---|
대략적인 전체 구조는 이렇습니다. Model Layer에서 두 부분으로 나눠지는데, 왼쪽의 경우 API 통신 없이 안드로이드 내장 DB를 사용하는 경우입니다. 이 부분에 대해서는 따로 다루겠습니다.
이번에 정리할 구조는 API를 사용하여 외부에서 데이터를 가져오는 구조입니다.
사실 왼쪽과 오른쪽은 데이터를 어디서 가져오느냐에 따라 추가로 레이어가 생기거나 축소되는 것일 뿐 큰 차이는 없습니다.
* 게시글 API를 통해 서버에서 데이터를 가져와 UI에 뿌려주는 상황
| 🧑💻 API InterfaceAPI를 정의Spring의 Conroller와 유사함 SQLitre의 ROOM과 역할이 비슷 |
|---|
@Singleton
interface BoardApiInterface {
@GET("/boards")
fun getAll(): Call<List<BoardResponse>>
@GET("/board")
fun getById(@Body id: Long): Call<BoardResponse>
}
Spring의 Controller처럼 url 및 method를 정의합니다. 이후 Repository에 넘겨줍니다.
| 🧑💻 Repositorynetwork 통신Spring Service와 유사함 |
|---|
@ActivityScoped
@Singleton
class BoardRepository @Inject constructor(
private val boardApiInterface: BoardApiInterface
){
fun getAll(callback: (Result<List<BoardResponse>>) -> Unit) {
boardApiInterface.getAll().enqueue(object : Callback<List<BoardResponse>> {
override fun onResponse(
call: Call<List<BoardResponse>>,
response: Response<List<BoardResponse>>
) {
if (response.isSuccessful) {
val boardResponse = response.body()
}
}
override fun onFailure(p0: Call<List<BoardResponse>>, p1: Throwable) {
TODO("Not yet implemented")
}
})
}
}
Repository는 API 톹신을 담당하는 부분입니다. 실질적으로 API를 호출하여 데이터를 받아보고, 응답을 처리하는 레이어입니다.
Spring의 Service와 유사한 레이어입니다.
| 🧑💻 ViewModel |
|---|
class TestViewModel @Inject constructor(
private val boardRepository: BoardRepository
) : ViewModel() {
// BoardApi 호출
fun loadBoards() = liveData<> {
emit(boardRepository.getAll())
}
}
Repository를 호출하여 데이터를 관리합니다.
| 🧑💻 View |
|---|
@Composable
fun TestView() {
val viewModel: TestViewModel = viewModel()
val boards by viewModel.loadBoards().observeAsState(initial = emptyList())
// Drew your UI
}
View는 실질적으로 UI를 그리는 부분과, viewModel을 호출하여 데이터를 불러와 관리합니다.
Questtion
| 🤔 1. 전체적인 구조가 맞는지?2. ViewModel 사용이유 ⚠️ 부분 |
|---|
Feedback
