[Android] derivedStateOf

MariGold·2일 전

[Android]

목록 보기
10/12
post-thumbnail

compose로 UI를 개발하다보면 상태 하나만 관리하면 될 줄 알았던 UI가 점점 복잡해지는 순간이 찾아옵니다.

  • 리스트는 그대로인데, 정렬된 버전이 필요
  • 입력값은 하나인데, 유효성 검증 결과 필요
  • 원본 데이터는 그대로지만, 펄터링된 결과값 표츌
  • 사용자가 스크롤할 때마다 계산해야하는 파생 값

이렇게 '원본 상태'외에도 그 상태로부터 파생되는 값들이 점점 늘어나면서 상태 관리가 어려워지고 코드도 복잡해지기 시작합니다.

특히 compose는 recomposition이 자주 발생하기 때문에 이런 파생 값들을 잘못 관리하면 불필요한 연산 + 성능 저하로 이어지기 쉽습니다. 그래서 이번 글에서는 Jetpack Compose에서 파생 상태를 효율적으로 다루는 derivedStateOf를 기본 개념부터 mutableStateOf와 비교, 실제 예제까지 정리해보겠습니다.


🚀 derivedStateOf란?

derivedStateOf는 Jetpack Compose에서 제공하는 파생 상태 계산 도구입니다. 다른 상태로부터 계산되는 값을 효율적으로 관리할 수 있도록 도와줍니다.

주요 특징

  • 메모이제이션(memoization) 기반 최적화
    -> 의존하는 값이 변경될 때만 재계산
    -> 같은 입력값에 대해서는 이전 계산 결과를 재사용
  • 비싼 연산 최적화에 특화
    ->필터링, 정렬, 파싱 등 성능에 민감한 작업에 유용
    -> Recomposition 중에도 불필요한 재계산 방지
  • 상태 중복 없음
    -> 파생된 값을 따로 상태로 저장할 필요 없음
    -> 원본 상태만 관리하면 파생 값은 자동으로 계산됨
  • 관찰 가능한(Observable) 상태
    -> 결과값이 변경되면 해당 값을 구독하는 컴포저블만 recomposition
    -> 불필요한 UI 업데이트 최소화

🔧 기본 사용법

@Composable
fun SearchScreen() {
    var searchQuery by remember { mutableStateOf("") }
    val items = remember { listOf("Apple", "Banana", "Cherry", "Date", "Elderberry") }
    
    val filteredItems by remember {
        derivedStateOf {
            items.filter { it.contains(searchQuery, ignoreCase = true) }
        }
    }
    
    Column {
        TextField(
            value = searchQuery,
            onValueChange = { searchQuery = it },
            label = { Text("검색") }
        )
        
        LazyColumn {
            items(filteredItems) { item ->
                Text(item, modifier = Modifier.padding(8.dp))
            }
        }
    }
}

동작 원리
1. searchQuery가 변경될 때만 filteredItems가 재계산됨
2. searchQuery가 같은 값으로 여러 번 recomposition되어도 한 번만 계산
3. 필터링 결과가 실제로 달라질 때만 LazyColumn이 업데이트됨


⚖️ mutableStateOf vs derivedStateOf

파생 상태를 관리할 때 mutableStateOf를 사용할지, derivedStateOf를 고민하게 되는데, 두 방식의 차이를 비교해보겠습니다.

mutableStateOf를 사용한 방식 (❌ 비효율적)

@Composable
fun SearchScreenWithMutableState() {
    var searchQuery by remember { mutableStateOf("") }
    val items = remember { listOf("Apple", "Banana", "Cherry", "Date", "Elderberry") }
    
    // mutableStateOf로 필터링 결과를 별도 상태로 관리
    var filteredItems by remember { mutableStateOf(items) }
    
    Column {
        TextField(
            value = searchQuery,
            onValueChange = { newQuery ->
                searchQuery = newQuery
                // 수동으로 필터링 결과 업데이트 - 실수하기 쉬움!
                filteredItems = items.filter { 
                    it.contains(newQuery, ignoreCase = true) 
                }
            },
            label = { Text("검색") }
        )
        
        LazyColumn {
            items(filteredItems) { item ->
                Text(item, modifier = Modifier.padding(8.dp))
            }
        }
    }
}

문제점

  • 상태를 두 번 관리해야 함 (searchQuery, filteredItems)
  • 업데이트 로직이 분산되어 있어 실수하기 쉬움
  • searchQuery를 다른 곳에서도 변경한다면 filteredItems 업데이트를 잊어버릴 수 있음
  • 메모리 낭비 (원본과 필터링 결과 모두 저장)

derivedStateOf를 사용한 방식 (✅ 효율적)

@Composable
fun SearchScreenWithDerivedState() {
    var searchQuery by remember { mutableStateOf("") }
    val items = remember { listOf("Apple", "Banana", "Cherry", "Date", "Elderberry") }
    
    // derivedStateOf로 자동 계산
    val filteredItems by remember {
        derivedStateOf {
            items.filter { it.contains(searchQuery, ignoreCase = true) }
        }
    }
    
    Column {
        TextField(
            value = searchQuery,
            onValueChange = { searchQuery = it },
            label = { Text("검색") }
        )
        
        LazyColumn {
            items(filteredItems) { item ->
                Text(item, modifier = Modifier.padding(8.dp))
            }
        }
    }
}

장점

  • 원본 상태(searchQuery)만 관리
  • 파생 값은 자동으로 계산되고 캐싱됨
  • 로직이 한 곳에 집중되어 유지보수 쉬움
  • 메모리 효율적 (계산 결과만 캐싱)

🎯 마무리

derivedStateOf는 Jetpack Compose에서 파생 상태를 효율적으로 관리하는 핵심 도구입니다. 이를 적절히 활용하면 불필요한 재계산을 방지하고, 코드를 더 선언적이고 유지보수하기 쉽게 만들 수 있습니다. 상태 관리가 복잡해질 때마다 "이 값은 다른 상태로부터 파생되는 것인가?"를 한 번 생각해고, 그렇다면, derivedStateOf가 여러분의 코드를 한층 더 깔끔하게 만들 수 있을 것입니다.


💡 참고자료

Jetpack Compose 공식 문서 - State
Compose Performance 가이드
derivedStateOf API 문서

profile
많은 것을 알아가고 싶은 Android 주니어 개발자

0개의 댓글