#1 개요
State Hoisting(상태 호이스팅) 패턴을 적용해 코드의 잠재적 유지보수성을 높혀본다.
#2 코드
#2-1 State Hoisting 패턴이 적용되지 않은 코드
...
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Box(
modifier = Modifier.fillMaxSize()
) {
ButtonExample(Modifier.align(Alignment.Center))
}
}
}
}
// ↓ ↓ ↓ Stateful Composable
@Composable
fun ButtonExample(modifierParam: Modifier = Modifier) {
val count = remember {
mutableStateOf(0)
}
Button(
onClick = { count.value++ },
modifier = modifierParam
) {
Text(
text = "Count: ${count.value}",
fontSize = 40.sp
)
}
}
위 코드의 ButtonExample()처럼 State를 보유한 Composable을 Stateful Composable이라고 부른다. Stateful Composable은 재사용성이 떨어지고 테스트하기도 어렵다는 문제를 가진다. 왜냐하면, 의존성 주입을 구현하지 않았기 때문이다. 이 문제를 해결하려면 State를 외부 생성자로부터 주입받는 방식으로, 즉 Composable이 생성자 주입(Constructor Injection)을 구현하게끔 변경해야 한다.
#2-2 State Hoisting 패턴이 적용된 코드
...
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Box(
modifier = Modifier.fillMaxSize(),
) {
val count = remember {
mutableStateOf(0)
}
ButtonExample(
count.value,
Modifier.align(Alignment.Center)
) { newValue ->
count.value = newValue + 1
}
}
}
}
}
// ↓ ↓ ↓ Stateless Composable
@Composable
fun ButtonExample(
currentCount: Int,
modifierParam: Modifier = Modifier,
updateCount: (Int) -> Unit,
) {
Button(
onClick = { updateCount(currentCount) }, // updateCount()는 ButtonExample()이 아닌, ButtonExample()를 호출한 상위 Composable에서 처리된다.
modifier = modifierParam
) {
Text(
text = "Count: $currentCount",
fontSize = 40.sp
)
}
}
State가 있는 Composable이 Stateful Composable이었던 것처럼, State가 없는 Composable은 Stateless Composable라고 부른다. State를 생성자 주입받는 경우도 Stateless Composable이다. 우선 State를 setContent { ... }로 이동시키고 ButtonExample의 매개변수에 State의 제네릭 타입(여기서는 Int)인 currentCount를 추가한다. 그리고 버튼의 클릭 리스너에 넣을 람다 함수도 매개변수로 추가한다. 이 람다 함수는 setContent { ... }에 있는 State의 값을 조작한다.
이렇게 만들어진 위 코드의 구조를 State Hoisting 패턴이라고 부른다. hoist의 사전적 의미는 (흔히 밧줄이나 장비를 이용하여) 들어[끌어]올리다인데, State를 하위 Composable (본 게시글에서는 ButtonExample())로부터 상위 Composable (본 게시글에서는 setContent)까지 끌어 당긴다는 늬앙스로 쓰인 것이다.
#3 State Hoisting 패턴의 의의
State Hoisting은 단방향 데이터 흐름(Unidirectional Data Flow)을 구축하는 데 주요하게 사용되는 패턴이다. 단방향 데이터 흐름이란, 상태(State)는 위(setContent)에서 아래(ButtonExample())로 보내고, 이벤트(클릭 이벤트 등)은 아래(ButtomExample())에서 위(setContent)로 보내는 흐름을 의미한다. 따라서 State Hoisting 패턴을 적용할 Composable에게는 2가지가 요구된다. Composable이 현재 표시할 값과 그 값을 변경 요청하는 이벤트다. 전자는 #2-2의 currentCount에 해당하고 후자는 #2-2의 updateCount에 해당한다.
단방향 데이터 흐름이 구축되면, 데이터베이스나 서버로부터 State를 받아와 Staless Composable에 주입시켜줄 수 있다는 점에서 캡슐화 및 모듈화가 달성된다. 또 State가 상위 Composable에 위치하기에 더 중앙집중적인 위치에서 State를 일괄적으로, 쉽게 관리할 수 있게 된다.
#4 요약
State Hoisting은 State를 상위 Composable로 옮기는 코드 패턴이다.
#5 완성된 앱
#6 이어지는 글
ViewModel을 이용함으로써, State Hoisting 패턴을 증대시킨다.
'깨알 개념 > Android' 카테고리의 다른 글
[Android] Coroutines Flow - Jetpack Compose에서 사용하기 (0) | 2024.08.06 |
---|---|
[Android] Jetpack Compose - ViewModel에서 State 사용하기 (0) | 2024.07.23 |
[Android] Jetpack Compose - State Remembering (0) | 2024.07.23 |
[Android] Jetpack Compose - State 기초 (0) | 2024.07.22 |
[Android] Jetpack Compose - Lazy Column (0) | 2024.07.22 |