#1 ๊ฐ๋ฐ ๋ชฉํ (Iteration Goal)
#1-1 ์ด์(Ideal)๊ณผ ์ง๊ฒ๋ค๋ฆฌ
์ด์(Ideal)
์ฌ์ฉ์ ๊ด์ ์์ ์ง๊ด์ ์ธ ๋ฉ๋ชจ ์ฑ์ ๋ง๋ค์ด์ผ ํ๋ค. ์ฌ์ฉ์์ ์๋์ ๊ฑธ๋ง์ ํ๋์ ์ ๋ํ ์ ์์ด์ผ ํ๊ณ , ์ฌ์ฉ์๊ฐ ์๋๊ฐ ๋๋ฌ๋๋ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ฃผ์ด์ผ ํ๋ค.
Iteration 3: ๋ฉ๋ชจ ํ๋ฉด์ State & Event ์ฒด๊ณ ์ ๋ฆฌ
์ง๊ธ๊น์ง ๊ตฌํํ UI๋ ์ผ์ข ์ ๋ผ๋์ ๋ถ๊ณผํ๋ค. ์ด ๋ผ๋์ ๋ถ์ผ๋งํ '์ด'์ด๋, ์ฌ์ฉ์ ํ๋์ ์ํ ์ํธ์์ฉ(Event) ๊ทธ๋ฆฌ๊ณ ์ํ(State)์ ๋ณํ๋ค. ์ฑ์์ ์ฌ์ฉ์์๊ฒ ์ ๊ณตํ '์ง๊ด์ ์ธ ํ๋'์ด ๋ฌด์์ธ์ง๋ฅผ ์ ์ํ๊ณ , (์ข์ ๋ฐฉํฅ์ผ๋ก) ์ ๋ฐ์ดํธ ํด๋๊ฐ์ผ ํ๋ค.
#1-2 ๊ฐ๋ฐ ๋ฒ์
์ด๋ฒ ๊ฐ๋ฐ ๋ฒ์ (In Scope)
- State down / Event up ๊ตฌ์กฐ ํ๋ฆฝ
- Screen์ ViewModel์ ๋ชฐ๋ผ์ผ ํจ
- ViewModel์ Compose State๋ฅผ ๋ชฐ๋ผ์ผ ํจ
- ViewModel ๊ตฌํ
- ViewModel ๋ณด์ ํ State์ UI ๋จ์์ ๋ณด์ ํ State (DrawerState ๋ฑ)๋ฅผ ๊ตฌ๋ถ
์ด๋ฒคํธ ๋ฐ์์ ๋ฐ๋ฅธ State ์ ๋ฐ์ดํธ๋ฅผ ์ํ onEvent() ํจ์ ๋ณด์- ์ ์ฒด ์ฝ๋์ ํ๊ท ๊ฐ๋ ์ฑ์ด ์ ํด๋๊ธฐ์ ์งํํ์ง ์์ (์ฐธ์กฐ: #2-2)
- MemoUiState ํด๋์ค ๊ตฌํ
- ViewModel์์ ๊ด๋ฆฌํ ๋ชจ๋ State๋ฅผ Wrapping ํ ํด๋์ค
MemoEvent ํด๋์ค ๊ตฌํEvent sealed interface- ์ ์ฒด ์ฝ๋์ ํ๊ท ๊ฐ๋ ์ฑ์ด ์ ํด๋๊ธฐ์ ์งํํ์ง ์์ (์ฐธ์กฐ: #2-2)
- MemoScreen ์
๋ฐ์ดํธ
- ๋งค๊ฐ๋ณ์๋ก State ๋ฐ Event๋ง ๋ฐ๋๋ก ์ ๋ฐ์ดํธ
๋ฒ์ ์ธ (Out of Scope)
- Data layer ๊ด๋ จ
- IME, focus, scroll ๊ด๋ จ
- UI ๋ค๋ฌ๊ธฐ
- ์ ๋๋ฉ์ด์
- ์ธ๋ถ ๋ ์ด์์ ๋ฏธ์ธ์กฐ์
- ๋์์ธ ๊ฐ์
- ์ฑ๋ฅ(์ต์ ํ) ๊ด๋ จ
- derivedStateOf ์ต์ ํ
- recomposition ๋ถ์
- snapshotFlow ์ฌ์ฉ
#2 ๋ณ๊ฒฝ ๋ด์ญ (์ปค๋ฐ ๋ชฉ๋ก)
#2-1 Iteration 3-1: ViewModel ๋์ ๋ฐ State ์ ๋ฆฌ
Swemo์ MemoScreen์๋ MemoEditor๊ฐ ์กด์ฌํ๊ณ , ์ด ์ปดํฌ๋ํธ์์ (์์ผ๋ก์ ๊ตฌํ์์) ๊ฝค ๋ณต์กํ State ๋ฐ Event๊ฐ ์กด์ฌํ ํฐ๋ค.
data class MemoUiState(
val categories: List<Category> = emptyList(),
val selectedCategory: Category? = null,
val memos: List<Memo> = emptyList(),
val allLabels: Set<String> = emptySet(),
val editorState: EditorState = EditorState()
) {
data class EditorState(
val isVisible: Boolean = false,
val editingMemo: Memo? = null
)
}
๊ทธ๋์, MemoUiState ์์ inner class๋ก EditorState๋ฅผ (๋ณ๋๋ก ๋ถ๋ฆฌํด) ์ ์ธํ๋ค.
์์กด์ฑ ๊ด๋ฆฌ๋ฅผ ์ํด Hilt ๋ฑ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ถ๊ฐํ๊ณ , ๋ง๋ฌด๋ฆฌ๋ก ์ด ์คํฌ๋ฆฝํธ๋ฅผ ํตํด์ Now in Android์ ๋ฒ์ ์นดํ๋ก๊ทธ์์์ ํ๋กํฐํผ๋ช ์ ๋ง์ถฐ์ฃผ์๋ค.
๊ด๋ จ ๊ฐ๋ฐ ๋ฒ์
- State down / Event up ๊ตฌ์กฐ ํ๋ฆฝ
- ViewModel ๊ตฌํ
- MemoUiState ํด๋์ค ๊ตฌํ
์ฃผ์ ๋ณ๊ฒฝ์
- MemoViewModel ๋์ (์์กด์ฑ ์ฃผ์ ์ Hilt๋ฅผ ์ด์ฉ)
- MemoUiState๋ก MemoScreen์ ๋ชจ๋ State๋ฅผ ๊ฐ์ฒด ํ๋๋ก ํตํฉ(Wrapping)
์ป์ ๊ฒฝํ์น
- MemoScreen ๋งค๊ฐ๋ณ์์ ๋ค์ด๊ฐ ๋๋ค ํจ์๋ฅผ ํจ์ ์ฐธ์กฐ(Callable Reference)๋ก ํ๊ธฐํด ๋ดค๋ค. ๊ฐ๋ ์ฑ ๋ฉด์์ ํจ์ฌ ๋์๋ค.
์ฐธ๊ณ
- [GitHub] ์ด ์์ ์ Commit
- [Tistory] Gradle, ๋ฒ์ ์นดํ๋ก๊ทธ
- [Tistory] ๋ด ํ๋ก์ ํธ์ Now in Android์ ๋ฒ์ ์นดํ๋ก๊ทธ ์ ํ๋กํผํฐ๋ช ๋ง์ถ๊ธฐ
#2-2 Iteration 3-2: Event ๊ด๋ฆฌ๋ฅผ ์ํ ๊ตฌ์กฐ ์ค๊ณ
#2-1์ ๋ง์ถฐ, ์์ ์ ๋ฐฐ์ด ๋ฐฉ์๋๋ก MemoEvent๋ฅผ ๋ง๋ค์ด์ Event ๊ด๋ฆฌ๋ฅผ ํ๋ ค๊ณ ํ๋ค.
// ์ถ๊ฐํ๋ ค๊ณ ํ๋ ์ฝ๋
sealed interface MemoEvent {
data class SelectCategory(val category: Category) : MemoEvent
data class AddMemo(val memo: Memo?) : MemoEvent
data class ChangeAddCategoryDialogVisibility(val visible: Boolean) : MemoEvent
sealed interface MemoEditor : MemoEvent {
data object ToggleVisibility : MemoEditor
}
}
์ด๋ ๊ฒ Event๋ฅผ sealed interface๋ก ๋ฌถ๋ ๊ฒ์ด ๋ณด๊ธฐ์๋ ํตํฉ์ฑ ์์ด ๋ณด์ธ๋ค. ์ดํ MemoViewModel์ fun onEvent(event: MemoEvent)๋ฅผ ์ ์ธํด์ MemoScreen์ผ๋ก๋ถํฐ Event๋ฅผ ๋ฐ๋ ๋ฐฉ์์ผ๋ก ๋ฐ๊พธ๋ ค ํ๋ค. ํ์ง๋ง ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด ๋ณด๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
// in MemoScreen.kt
ModalNavigationDrawer(
...
drawerContent = {
CategorySelector(
...
onAddCategoryButtonClick = {
// onAddCategoryDialogVisibilityChange(true)
onEvent(MemoEvent.ChangeAddCategoryDialogVisibility(true)) // ๋ณต์กํด์ก๋ค
}
)
}
)
์ฃผ์ ์ฒ๋ฆฌ๋ ์ฝ๋๊ฐ ์๋ ์ฝ๋์ธ๋ฐ, ๊ทธ ์๋ ์ฝ๋์ ๊ฐ์ด ๊ฐ๋ ์ฑ์ด ๋์ ์ฝ๋๋ก ๋ฐ๊ฟ์ฃผ์ด์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ผ์, ๋์ค์ Event๊ฐ ์ ๋ง ๋ง์ด ๋ณต์กํด์ ธ์ ์ง์ค ๊ด๋ฆฌ๋ฅผ ํด์ค์ผ ํ ํ์๊ฐ ์๊ธฐ๊ธฐ ์ ๊น์ง๋ (MemoEvent ๋ฑ์ผ๋ก) Event๋ค์ ๋ฌถ์ง ์๊ธฐ๋ก ํ๋ค.
๊ฒฐ๋ก ์ ์ผ๋ก ์ด๋ฒ ์ปค๋ฐ์์ , ์์ง MemoScreen์ ๋จ์์๋ ์ด๋ฒคํธ ์ ์ธ๋ถ๋ค์ ViewModel๋ก ์ฌ๋ฆฌ๊ณ ์ฝ๋๋ฅผ ์ฝ๊ฐ ๋ค๋ฌ๋ ์ ๋๋ก ๋ง๋ฌด๋ฆฌํ๋ค.
๊ด๋ จ ๊ฐ๋ฐ ๋ฒ์
- State down / Event up ๊ตฌ์กฐ ํ๋ฆฝ
์ฃผ์ ๋ณ๊ฒฝ์
- MemoScreen์ ๋จ์์๋ ์ด๋ฒคํธ ์ ์ธ๋ถ๋ค์ ViewModel๋ก ์ฌ๋ฆผ
์ฝ๋ ์ค๋ํซ - MemoScreen ํธ์ถ๋ถ
MemoScreen(
uiState = uiState,
drawerState = drawerState,
// events
onCategorySelected = viewModel::selectCategory,
onAddMemoClick = { viewModel.addMemo() },
onMemoEditorToggleButtonClick = viewModel::toggleEditorVisibility,
onAddCategoryDialogVisibilityChange = viewModel::changeAddCategoryDialogVisibility,
)
Event๊ฐ ํ๋ ์ถ๊ฐ๋ ๋๋ง๋ค ๋๋ค ํจ์ ๋งค๊ฐ๋ณ์๊ฐ ํ๋์ฉ ๋์ด๋ ๊ฒ์ด๋ค. MemoEvent ๋ฐ MemoViewModel.onEvent()๊ฐ ์์๋ค๋ฉด, ๋ชจ๋ ์ด๋ฒคํธ ๋๋ค ํจ์๋ฅผ onEvent = viewModel::onEvent ๋ก ํ์น ์ ์์์ ๊ฒ์ด๋ค. ํ์ง๋ง ์ด๋ฐ ์ด๋๋ณด๋ค, ๋ค๋ฅธ ๋ถ๋ถ์์ ๋ฐ์ํ๋ ๊ฐ๋ ์ฑ์ ์ธ ์์ค์ด ๋ ํด ๊ฒ์ด๋ค.
์ป์ ๊ฒฝํ์น
- ๋น์ฐํ๊ฒ๋, ํจ์ ์ฐธ์กฐ(Callable Reference)๋ ๋งค๊ฐ๋ณ์๊ฐ ์์ ๋๋ง ์ธ ์ ์๋ค.
์ฐธ๊ณ
- [GitHub] ์ด ์์ ์ Commit
- [Tistory] [Android] Jetpack Compose - ๊ฐ์ฒด ์งํฅ์ UI ๋ ์ด์ด ์ค๊ณ
#2-3 Iteration 3-3
์ถ๊ฐ ์์ .
#3 ํ ์คํธ
์ถ๊ฐ ์์ .
#4 ๊ฐ๋ฐ ๊ฒฐ๊ณผ (Iteration Outcome)
์ถ๊ฐ ์์ .
'๊ฐ๋ฐ ์ผ์ง ๐ป > Swemo' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| Swemo - Iteration 2: UI ๋์์ธ ๋ฐฉํฅ ํ์(Exploration) (0) | 2026.01.13 |
|---|---|
| Swemo - Iteration 1: ๋ฉ๋ชจ ์ฑ์ ์ฒญ์ฌ์ง(๊ธฐ์ด) (0) | 2025.12.28 |
| Swemo - Nutri Capture ๊ฒ์๊ธ ์๋ฆฌ์ฆ๋ก ์ด๋ (0) | 2025.12.09 |
| Nutri Capture - Swemo ๊ฒ์๊ธ ์๋ฆฌ์ฆ๋ก ์ด๋ (0) | 2025.12.09 |
| Nutri Capture - ๋ฐฉํฅ ์ฌ์ค๊ณ (ํ๋ก์ ํธ ์ด๋ฆ ๋ณ๊ฒฝ) (0) | 2025.12.02 |