State vs StateFlow vs SharedFlow

권민주·2025년 7월 19일

안드로이드

목록 보기
15/23
post-thumbnail

1. State

1)제공처

  • Jetpack Compose

2)초기화

  • mutableStateOf(value)
  • 보통 remember { mutableStateOf(...) } 또는 rememberSaveable { ... } 형태

3)주 사용처

  • Composable 내부의 로컬 상태
  • UI 전용 상태(스크롤, 포커스, 토글, 임시 입력값 등)

4)비동기 스트림

  • 비동기 처리에는 부적합
  • State 자체가 Flow가 아니므로 비동기 스트림 처리가 불가능
  • 비동기 로직 결과를 담는 그릇의 가능

5)재구성 트리거

  • 값이 변경되면 자동으로 Composable 재구성

6)단일/복합 데이터

  • 단일 값 관리에 적합
  • 복합 데이터도 사용할 수 있지만 그 정도의 데이터는 책임과 수명이 큰 편이기에 보통 ViewModel의 StateFlow로 관리하는 게 더 적합
  • Composable 내부에 상태가 많아지면 관리가 분산, 수동 관리가 많아짐

7)속성 위임(by)

  • by 키워드로 사용 가능
  • var count by mutableStateOf(0)

8)스레드 안전성

  • Compose 내부에서만 동작하기에 UI 스레드에서만 사용 가능
  • UI 스레드 외에서 값 변경 시 예외 발생 가능

9)초기 값 유지

  • Composable의 재구성에는 값 유지 가능

2. StateFlow

1)제공처

  • Kotlin Coroutines의 Flow API

2)초기화

  • MutableStateFlow(value)

3)주 사용처

  • ViewModel 등에서 UI와 상태 관리 및 공유
  • 현재 상태가 항상 존재해야 하는 화면 상태

4)비동기 스트림

  • 비동기 상태 흐름에 적합
  • Flow 기반 스트림
  • 여러 collector가 동시에 구독 가능

5)재구성 트리거

  • 기본적으로 재구성X. Compose는 StateFlow를 직접 관찰하지 않음
  • collectAsState()로 구독하면 State으로 값이 변경. 이후 값이 변경될 때 재구성
  • 비동기적으로 작동하기 때문에 재구성이 곧바로 적용되지 않을 수 있음

6)단일/복합 데이터

  • Flow 기반 스트림으로, 내부 값이 변경될 때마다 구독자에게 emit하고 UI가 collect하는 구조
  • 상태를 한 곳에서 단방향으로 흐르게 만들기 때문에 상태 추적이 쉬워짐. 그래서 복합 데이터(data class등) 관리에 유리
  • 최신 값 1개를 항상 유지하는 상태 스트림

7)속성 위임(by)

  • by 사용 불가능. 내부적으로 위임 인터페이스 구현 X
  • collectAsState() 사용하여 State형태로 바꾼 후 위임 사용 가능

8)스레드 안전성

  • Coroutine 기반이기에 스레드 안전
  • 여러 coroutine에서 사용 가능
  • 상태 업데이트는 보통 ViewModel scope에서 단일 진입점으로 관리 추천

9)초기 값 유지

  • 항상 초기값이 존재
  • 새 collector도 즉시 최신값을 받음
  • ViewModel 등과 함께 사용 시 상태 보존 용이

3. SharedFlow

1)제공처

  • Kotlin Coroutines의 Flow API

2)초기화

  • MutableSharedFlow(replay = 0, extraBufferCapacity=0)
  • replay
    • 기본 값으로 0을 가짐
    • 새로 구독한 collector에게 과거 값을 몇 개 다시 보내줄지 설정하는 값
    • replay가 1이면 새 collector가 붙으면 가장 최근 이벤트 1개를 다시 받음. UI 이벤트에 쓰면 중복 실행 위험이 생겨 비추천
  • extraBufferCapacity
    • 기본 값으로 0을 가짐
    • collector가 느리거나 아직 없을 때, emit되는 값을 추가로 몇 개까지 버퍼에 쌓아둘지 설정
    • 값을 설정하면 collector가 처리하기 전이라도 일정 개수까지는 임시로 저장. UI 이벤트처럼 잠깐 밀릴 수 있는 이벤트에 유리

3)주 사용처

  • 일회성 액션의 UI 이벤트 전달(예. 토스트/스낵바, 네비게이션, 다이얼로그 표시 트리거)
  • 여러 구독자에게 브로드캐스트
  • 이벤트를 넣을 때 emit()tryEmit()존재
    • emit()는 suspend이지만 tryEmit()는 즉시 반환하여 버퍼가 꽉 차면 실패
    • 유실되면 안 되는 이벤트에는 emit(), 유실돼도 되는 이벤트에는 tryEmit() 사용

4)비동기 스트림

  • 이벤트 스트림에 적합
  • 상태가 아니라 발생을 흘려보내는 용도

5)재구성 트리거

  • UI 상태가 아니므로 보통 재구성 트리거로 쓰지 않음
  • UI에서 collect로 받아서 동작을 실행하는 형태가 일반적

6)단일/복합 데이터

  • 단일/복합 모두 가능하지만 어차피 의미는 이벤트 전달
  • 같은 값이 여러 번 emit돼도 문제 없음. state처럼 덮어쓰기 개념이 아님

7)속성 위임(by)

  • 불가능
  • UI에서는 보통 LaunchedEffect로 collect
  • LaunchedEffect(Unit) {
      viewModel.events.collect { event -> ... }
    }

8)스레드 안전성

  • Coroutine 기반이기에 스레드 안전
  • 이벤트 유실/버퍼 정책은 설정에 따라 달라짐(replay/buffer)

9)초기 값 유지

  • 기본적으로 현재 값 없음
  • replay는 기본 값으로 0을 가지기에 새 collector는 과거 이벤트를 못 받음

3. State vs StateFlow vs SharedFlow

항목StateStateFlowSharedFlow
제공처ComposeCoroutineCoroutine
초기화mutableStateOf()MutableStateFlow()MutableSharedFlow()
주사용처Composable 내부 상태UI와 상태 공유
(주로 ViewModel → UI)
이벤트/신호 전달
(주로 ViewModel → UI)
비동기 스트림부적합적합적합
재구성값 변경 시 자동으로 Composable
재구성
자체로는 재구성 불가 →
collectAsState()로 State로 변환 후 재구성
자체로는 재구성 불가 →
collectAsState() 또는
collectAsStateWithLifecycle()로 반영
단일/복합 데이터단일 데이터에 적합단일 현재 상태에 적합단발성 이벤트/여러 이벤트 흐름에 적합
속성 위임가능 (by)가능 (byState<T>에만 직접 가능)불가능 (State<T>로 변환해야 by 가능)
스레드 안정성UI 스레드에서만 사용 권장안정적안정적
초기값 유지유지 가능유지 가능 (ViewModel 등과 사용 시 유지)기본적으로 초기값 없음(이벤트 중심), replay로 최근 값 재전달 가능
값 개념현재 값현재 값이 항상 존재방출된 이벤트들 (현재값 개념 없음)
초기값 필요 여부필요필요불필요
최신 값 접근state.valuestateFlow.value불가(현재값 없음)
구독 시작 시 동작즉시 현재값 사용즉시 현재값 1개 받음기본은 아무것도 못 받음 (replay 설정 시 최근 이벤트 수신)
주요 옵션--replay, extraBufferCapacity, onBufferOverflow
대표 용도 예시텍스트 입력, 토글 상태화면 상태, 로딩/데이터/에러 상태토스트, 네비게이션, 스낵바, 1회성 메시지, 클릭 이벤트 전달
profile
안드로이드 개발자:D

0개의 댓글