본문 바로가기

Android/Jetpack Compose

[Jetpack] Compose 사용하기 - 1. remember와 MutableState

728x90

Jetpack Compose를 공부하며 예제를 만들어보는 도중, 처음 접하게 되는 키워드들과 클래스, 함수들이 상당히 많았다.

해당 키워드와 클래스들을 사용하지 않으면 Compose를 사용하는 것에 무리가 있을 것으로 판단이 되었고, 따라서 예제를 만들어 보면서 기본적인 것부터 정리해 나가려고 한다.

 

이번 글에서는 그 중 가장 처음에 접하게 되었던 remember과 MutableState에 대하여 사용 방법을 정리해 보았다.


 

우선,

기본적으로 Compose에서 어떠한 상태 값이 바뀌게 되면 재구성(Recomposition)이 일어나게 된다.

여기서 재구성이란, 말 그대로 재 생성한다는 뜻이다.

 

예로 들어, a라는 값을 기본으로 가지고 있고 버튼을 누르면 b라는 값으로 변경되는 TextView가 있다고 가정해 보자.

버튼을 누르게 되면 a라는 값이 b라는 값으로 상태 값이 바뀌게 되는데, 이때 재구성이 일어나게 되어 UI를 다시 그리게 된다.

다시 그리게 되면 b라는 값을 가지게 되는게 아니라, 기본 값인 a가 나오게 되어 사용자가 원하는 동작이 이루어지지 않게 된다.

 

따라서, 재구성이 되었을 때도 값을 저장할 수 있도록 하기 위하여 Compose에서는 remember 키워드를 제공한다.

 

val a = remember { mutableStateOf(false) }
var b by remember { mutableStateOf("a") }
val (c, d) = remember { mutableStateOf("") }

 

이와 같이 3가지 방법으로 remember을 선언하여 사용한다.

 

각 선언 방법을 설명하기 앞서, mutableStateOf 라는 키워드도 처음 접할 것이다.

mutableStateOf에 대한 설명을 확인해보면 다음과 같이 나와있다.

MutableState 클래스는 Compose에 의해 읽기와 쓰기를 관찰하는 단일 값 보유자입니다.


Compose에서 상태를 저장하고 상태가 변경 되었을 때 재구성하기 위해서는 관찰 가능한 객체를 사용해야 하는데, MutableState 클래스는 Compose에서 읽기와 쓰기를 관찰하기 위해 만들어진 클래스라고 생각하면 된다.

 

다시 위의 변수를 확인해보자.

a는 아주 기본적으로 사용하는 방법으로, mutableStateOf(~)에 들어가는 ~는 해당 변수의 Default 값이라고 생각하면 된다.

즉, a라는 변수의 기본 값은 false이고 해당 값을 관찰하고 저장할 예정이라 재구성돼도 default 값으로 저장되지 않는다.라는 뜻이 된다.

 

b는 by라는 키워드를 사용하여 get,set을 b에 위임하도록 만들어서 사용하는 방법이다.

여기서 주의해야 할 점은, by 키워드를 사용하는 경우 반드시 var로 선언해야 한다는 점이다.

 

이것에 대하여는 다음 사용하는 코드를 보면 쉽게 이해가 가능하다.

 

var isExpanded by remember { mutableStateOf(msg.open) }
val temp = remember { mutableStateOf(msg.open) }

temp.value = msg.open
isExpanded = msg.open

 

필자가 예제로 만들어 둔 코드의 일부이다.

isExpanded와 temp의 값을 갱신하는 부분을 확인해 보면 된다.

by 키워드를 통하여 사용한 isExpanded는 해당 변수에 바로 값을 저장하는 반면,

temp의 경우 .value 를 사용하여 값을 갱신하도록 하고 있다.

 

 

이처럼 by 키워드를 사용하게 되면 get/set에 대한 위임이 이루어지기 때문에 isExpanded라는 변수 자체가 값을 변경하고 사용할 수 있기 때문에 값을 변경할 수 있는 var로 선언해서 사용해야 한다.

 

*

by 키워드를 사용하여 선언 하였을 때, import 되는 것 없이 오류만 계속 뜬다면 해당 import가 되어있는지 확인하길 바란다.

import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue

 

마지막으로, 세번째 사용하는 방법은 람다식을 하나 추가하여 사용하는 방법이다.

조금 사용법이 다르기 때문에 설명부터 확인해보도록 하자.

 

 

위의 설명을 보면 알 수 있겠지만, 간단하게 말하자면 값이 변경되면 변경된 값을 저장하고 같은 값으로 변경되면 재구성을 하지 않도록 해준다고 한다.

 

해당 사용법은 TextField를 사용할 때 아주 유용하게 쓰이는데, 예제 코드를 보면서 이해해보도록 하자.

 

val (c, d) = remember { mutableStateOf("") }
TextField(value = c, onValueChange = d)

 

TextField에서 값을 입력받으면 value 값이 계속해서 갱신되게 된다.

그렇게 되면 앞서 말한 재구성이 일어나게 되는데, 이미 작성된 텍스트와 새로 입력받은 텍스트를 저장하지 않으면 정상적으로 글을 작성할 수 없게 되므로 입력받은 값을 추가하여 텍스트 값을 저장해주어야 한다.

 

이때, 텍스트 값을 변경하는 부분이 onValueChange인데 해당 인자에는

onValueChange: (String) -> Unit

 

이와 같이 람다식이 들어가야 한다.

해당 람다식을 보고 다시 위의 MutableState를 보면 비슷한 부분을 확인할 수 있을 것이다.

 

즉, 해당 방식을 사용하여 변수를 선언하게 되면, 별도의 람다식을 넣지 않아도 텍스트 필드에 값이 변경되면 변경된 값을 정상적으로 넣어주게 된다.

 

조금 더 이해를 돕기 위해 다른 사용법으로 람다식을 구현하면 다음과 같게 된다.

 

var b by remember { mutableStateOf("") }
TextField(value = b, onValueChange = { change -> b = change})

 

TextField를 사용하여 키보드로부터 입력을 받으면, 변경 된 값을 b에 저장하고 재구성됐을 때 해당 값을 사용하도록 해야 하므로 onValueChange에 작성한 것처럼 람다식을 추가해야 한다.

 

위의 람다식을 사용하지 않고 사용할 수 있도록 만들어둔 방법이 세 번째 선언 방식으로 가장 간단한 사용 방법인 TextField를 예로 들어 설명을 했지만, 상황에 맞춰서 알맞은 방식으로 선언하여 사용하면 된다.


필자가 Compose를 사용하면서 처음에는 단순히 이런저런 UI만 그리면서 테스트를 해보다가,

변수를 사용하여 값을 컨트롤하려고 할 때 처음부터 하는 방법을 알 수 없어서 적지 않은 충격을 받았다.

 

Compose는 그냥 UI만 그리는 도구 같은 느낌으로 다른 차이점은 전혀 없다고 생각했는데 그런 것이 아니었고,

다시 한번 사용하는 예제들을 확인해보니 처음 보는 것들이 너무나도 많았다.

 

따라서, 처음 Java나 Kotlin을 공부할 때 기본적인 것부터 차례대로 공부했듯이,

Compose에 관한 것들도 기본적인 것들부터 차례대로 공부하고 정리해 나가야 할 것 같다.

728x90