본문 바로가기

Android/Tech

[Jetpack Compose] 상태 호이스팅

Unsplash, Ksenia Kudelkina.

 

기존 XML 방식에는 큰 문제점이 하나 있었는데, 그것은 바로 View 가 스스로의 상태를 정의하고 보존한다는 것입니다. 상태는 View 를 그 자체로 인식할 수 없도록 하며, 이는 곧 테스트 가능성과 재사용성을 저해하는 원인이 됩니다.

 


State Hoisting

Jetpack Compose 를 활용하여 UI 를 작성할 때에 사용할 수 있는 패턴입니다. Composable 은 XML 방식의 View 와 다르게, 그 자체로는 Stateless 상태입니다. 얼마든 재사용할 수 있고, 얼마든 테스트할 수 있습니다.

 

다만, Composable 이 특정한 상태가 부여되거나 특정 상황에 결속되는 경우가 있습니다. 전자는 TextField, Scrollable 한 Composable 이 해당하고, 후자의 경우 클릭 이벤트를 처리하는 코드가 포함된 Composable 등 입니다.

 

해당 Composable 들의 경우, 클릭에 대한 액션이나 스크롤, 또는 값 저장(TextField) 로직을 Composable 내부에 두게 되면 해당 Composable 과 상태는 서로에 대해 상호 의존성을 갖게되며, 테스트가 불가하고 재사용할 수 없게 됩니다.

 

또한, State 와 관련한 코드가 경우에 따라 다른 객체 등을 참조할 수가 있는데, 그러한 경우 당연스럽게도 재사용할 수 없고, 테스트시에도 별도의 Mock 객체를 생성해야하는 등의 수고가 뒤따릅니다.

 

이러한 문제를 해결하기 위해 State Hoisting 을 사용할 수 있습니다.

 


 

왜 Hoisting 이란 용어를 썼는가에 대한 짐작을 해보자면, Hoisting 은 끌어 올리다, 견인하다 라는 뜻입니다.

Android Developer.

 

Stateful 하게 만들어질 수 있는 Composable 의 State 를 상위 수준에서 해결할 수 있도록 파라미터를 설정하는 방식입니다. 이렇게 하면 해당 Composable 을 호출하는 상위 스코프에서 Composable 의 상태를 설정하거나 이벤트를 관리할 수 있습니다.


 

파라미터를 넘기는 것은 크게 다룰 만한 내용이 없어 서술하지 않습니다. 중요한 건 이벤트인데요, 이벤트를 다루는 State Hoisting Pattern 의 구현을 위해, 우리는 람다를 사용할 수 있습니다. 즉, Composable 파라미터에 함수 파라미터를 사용하는 것입니다.

 

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ExampleComposeTheme {
                ExampleComposable { // onClick() 람다
                    // do something.
                }
            }
        }
    }
}

@Composable
fun ExampleComposable(onClick: () -> Unit) {
    Text("Example.", modifier = Modifier
        .width(300.dp)
        .height(200.dp)
        .clickable {
            onClick()
        })
}
 

 

이와 같이 구현하면 해당 Composable 은 onClick() 함수 블럭을 호출부에서 정의할 수 있습니다.

Composble 은 클릭시 onClick() 함수를 호출할 뿐, 어떠한 상태도 가지지 않습니다. 실행되는 코드는 상위 스코프에서 지정해준 이벤트 코드이고요.

 


 

StateHoisting 은 Composable 재사용과 테스트를 위해 사용됩니다. 간단한 테스트만 하려고 해도, 특정 값을 내부에서 캡처하여 참조하는 경우, 클릭 이벤트가 내부에 정의되어있는 경우 등의 Composable 은 Preview 가 지원되지 않기 때문입니다.

 

Jetpack Compose 는 이름처럼 조합에 주안점을 둔 UI 툴킷이니, 재사용 가능성이 있거나 다수의 테스트가 보장되어야만 하는 Composable 이라면 State Hoisting 을 통해 Stateless 하게 관리하는 것이 중요해 보입니다.