Intro
- 이제 MVVM구조로 프로젝트를 만드는 연습중이다. 오늘 domain영역까지 어떻게 구현할지 배우는시간이었다.
- 현재 밑바닥에서부터 쌓아가는 연습을 시작했다.
TodoList 반복구현
- 우선 TodoList에 들어가는 기능은 아래와같다.
- 할일 목록에 item 추가/수정/삭제
- 할일 목록추가는 다른 Activity에서 EditText로 사용자에게 입력받은 값으로 함.
- 할일 목록 item의 switch를 On 할 경우 Bookmark의 item 추가
- 할일 목록 item의 switch를 off할 경우 Bookmark의 item 삭제
- 여기에 2023년 상반기 카뱅 입사과제 추가
- 지금 블로그를 쓰는 시점에서는 item의 CRUD가 구현된 상태이다. 이제 switch로 bookmark에 item을 추가/제거 하는 부분을 구현하는 중이다.
- 코드가 너무 많아 다 붙여넣기에는 말도안될것같아 만들면서 햇갈리거나 정립이 잘 되지않았던 부분들 위주로 정리해보려고 한다.
Intent
- 나는 지금까지 인텐트를 아래와같은 방식으로 사용했었다.
val intent = Intent(context, MainActivity::class.java)
intent.putExtra("extra_todo_item", item)
mainLauncher.launch(intent)
val todoModel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
result.data?.getParcelableExtra(
ContentActivity.EXTRA_TODO_MODEL,
TodoModel::class.java
)
} else {
result.data?.getParcelableExtra(ContentActivity.EXTRA_TODO_MODEL)
}
- 위와같이 사용하는것 나쁘지 않다. 그런데 이것보다 더 효율적이고 가독성이 좋은 방법을 보게되어버렸다..!
companion object {
const val EXTRA_TODO_MODEL = "extra_todo_model"
const val EXTRA_TODO_ENTRY_TYPE = "extra_todo_entry_type"
const val EXTRA_TODO_POSITION = "extra_todo_position"
fun newIntentForAdd(context: Context): Intent =
Intent(context, ContentActivity::class.java).apply {
putExtra(EXTRA_TODO_ENTRY_TYPE, ContentType.Add.name)
}
fun newIntentForEdit(
context: Context,
position: Int,
todoModel: TodoModel,
): Intent =
Intent(context, ContentActivity::class.java).apply {
putExtra(EXTRA_TODO_ENTRY_TYPE, ContentType.Edit.name)
putExtra(EXTRA_TODO_POSITION, position)
putExtra(EXTRA_TODO_MODEL, todoModel)
}
}
private val entryType by lazy {
intent.getStringExtra(EXTRA_TODO_ENTRY_TYPE)
}
private val position by lazy {
intent.getIntExtra(EXTRA_TODO_POSITION, -1)
}
private val todoModel by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(EXTRA_TODO_MODEL, TodoModel::class.java)
} else {
intent.getParcelableExtra(EXTRA_TODO_MODEL)
}
}
private val addTodoLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
val todoModel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
result.data?.getParcelableExtra(
ContentActivity.EXTRA_TODO_MODEL,
TodoModel::class.java
)
} else {
result.data?.getParcelableExtra(ContentActivity.EXTRA_TODO_MODEL)
}
viewPagerAdapter.getTodoFragment().addTodoModel(todoModel)
}
}
- 이전에 내가 사용했던 방식은 분기처리가 없는 방식이다. 그러다보니 직관성이 떨어진다. 반면 개선된 코드의 특징은 분기처리를 한다는것이다. 분기관리는 sealed class를 사용하여 관리한다. sealed class의 특징은 제한된 클래스 라는것인데, 이는 컴파일러가 sealed class에 어떤 것이 존재하는지를 알고있다는 뜻이다. 그래서 기존에 const val 형식으로 선언했던 것들과 다르게 when 절에서 sealed class에 선언한 것들만 정확하게 불러오고 선언되지않은것을 가져오거나 선언을 누락할경우 에러를 볼 수 있다.
- 그리고 또한가지는 클래스에 intent를 선언하면서 필요한 값들을 편하게 주고받을 수 있다는것이다. 사실 이것때문에 초반에 머리가 터질뻔했다.. 나는 intent를 사용하면서 늘 단방향 통신만 생각했었는데 쌍방통신을 구현하려니 어질어질했다... 어쨋든, 쌍방통신에서 필요한 값들을 실어나르고 받는게 개선된 방식으로 사용하면 굉장히 직관적인 코드가된다. 사용은 ContentActivity에 선언된 entryType부터 보면된다. 해당 항목들을 lazy하게 선언한 이유는 이게 더 안전하다고 생각해서였다. 사용하는 순간에 해당항목에 접근하게되니 내가 사용타이밍과 순서만 잘 지키면 오류날일 없이 잘 돌아간다고 생각했다.
Outro
- 이제 남은시간동안(언제끝날지 아무도 모르지만..) MVVM의 완성을 향해 달려보려고한다. 책으로치면 1회독을 해보려고한다.
- 내가 하나의 개념을 어느정도 이해하고 생각해서 코드를 구현하는데 얼마나 걸리나보니까 4회독정도 같은 코드를 구현하면서 공부하면 그 뒤로부터는 기본적인 사용법은 숙지가 되더라..! 이번 MVVM은 얼마나걸릴지 모르겠지만 최종프로젝트 전에 뿌셔보려고한다. 잘 뿌시면 내친김에 di까지 적용해보는게 내 목표이다..!