[Compose SideEffect] SideEffect를 이용해 부수효과 처리하기

2021. 12. 20. 22:59· Android Jetpack Compose/Compose Side Effect
목차
  1. SideEffect란
  2. SideEffect의 사용
  3. SideEffect의 한계점
  4. 참조
반응형

SideEffect란

SideEffect는 Composable의 Composition이 성공적으로 되었을 때 발행하는 Effect이다. SideEffect는 Compose에서 관리하지 않는 객체와 Compose 내부의 데이터를 공유하기 위해 사용한다. 

 

 

SideEffect의 사용

TextField를 보이지 않게(isVisible=false) 했다가. 버튼이 눌렸을 때 보이게(isVisible = true) 만드는 코드가 있다고 하자. 이때 isVisible과 관련된 값은 모두 Compose에서 관리하는 값이므로 다음과 같이 작성이 가능하다.

@Composable
fun HomeScreen() {
    var isVisible by remember { mutableStateOf(false) }

    Column(modifier = Modifier.fillMaxSize()) {
        Button(onClick = { isVisible = true }) {
            Text(text = "버튼 클릭")
        }
        if (isVisible) {
            OutlinedTextField(value = "", onValueChange = {})
        }
    }
}

 

 

이번에는 isVisible일 때 TextField에 Focus를 주는 코드를 만들어보자. 단순히 생각하면 TextField에 focusRequester을 붙여놓고 isVisible일 때 focusrequester.requestFocus()를 수행하면 된다.

@Composable
fun HomeScreen() {
    var isVisible by remember { mutableStateOf(false) }
    val focusRequester = remember { FocusRequester() }

    Column(modifier = Modifier.fillMaxSize()) {
        Button(onClick = { isVisible = true }) {
            Text(text = "버튼 클릭")
        }
        if (isVisible) {
            OutlinedTextField(
                modifier = Modifier
                    .fillMaxSize()
                    .focusRequester(focusRequester),
                value = "", onValueChange = {})
            focusRequester.requestFocus()
        }
    }
}

 

하지만 위의 코드는 다음과 같은 오류를 내며 앱을 강제 종료시킨다., 

2021-12-20 16:59:28.049 10431-10431/com.simpli.compose E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.simpli.compose, PID: 10431
    java.lang.IllegalStateException: 
       FocusRequester is not initialized. Here are some possible fixes:
    
       1. Remember the FocusRequester: val focusRequester = remember { FocusRequester() }
       2. Did you forget to add a Modifier.focusRequester() ?
       3. Are you attempting to request focus during composition? Focus requests should be made in
       response to some event. Eg Modifier.clickable { focusRequester.requestFocus() }
    
        at androidx.compose.ui.focus.FocusRequester.requestFocus(FocusRequester.kt:54)
        at com.simpli.compose.MainActivityKt.HomeScreen(MainActivity.kt:49)
        at com.simpli.compose.MainActivityKt$HomeScreen$2.invoke(Unknown Source:4)
        at com.simpli.compose.MainActivityKt$HomeScreen$2.invoke(Unknown Source:10)

 

여기서 주의 깊게 봐야할 부분은 이 부분이다.

java.lang.IllegalStateException: FocusRequester is not initialized. Here are some possible fixes:

 

바로 FocusRequester가 Initialized 안되었다는 것이다. Composable은 선언형 프로그래밍 패러다임을 따른다. 선언형 프로그래밍 패러다임은 Composable 구성 순서를 보장하지 않으며 결과만을 보장한다. 따라서 focusRequester가 초기화되기 전에 isVisible이 true로 됨으로 인해 focusRequester에 대한 requestFocus 요청이 들어갈 경우 initialized 되지 않았다는 오류가 뜬다.

 

위의 FocusRequester의 requestFocus는 Composable이 아닌 시스템의 이벤트이므로 Composable이 관리하는 이벤트가 아니다. 따라서 Composable의 구성이 완료된 후에 requestFocus가 호출되도록 보장하려면 다음과 같이 SideEffect를 사용해야 한다. 

@Composable
fun HomeScreen() {
    var isVisible by remember { mutableStateOf(false) }
    val focusRequester = remember { FocusRequester() }

    Column(modifier = Modifier.fillMaxSize()) {
        Button(onClick = { isVisible = true }) {
            Text(text = "버튼 클릭")
        }
        if (isVisible) {
            OutlinedTextField(
                modifier = Modifier
                    .fillMaxSize()
                    .focusRequester(focusRequester),
                value = "", onValueChange = {})
        }
    }

    SideEffect {
        if (isVisible) {
            focusRequester.requestFocus()
        }
    }
}

 

위 코드를 수행하면 다음과 같이 작동한다.

 

그림1. SideEffect

 

* 안드로이드 키보드의 타이밍 이슈로 인해 이 키보드가 올라오는 코드가 동작하지 않을 수 있다. 이런 경우 requestFocus 전에 일정 시간의 delay를 주면 해결되지만, 더욱 좋은 방법을 찾기를 바란다.
* 물론 이렇게 SideEffect를 사용하는 것은 권장되지 않는다. 이유는 아래에서 마저 설명한다.

 

SideEffect의 한계점

SideEffect는 Composable이 구성 완료되었을 때 실행된다. 하지만 리컴포지션이 일어날 때마다 수행되며 이는 LaunchedEffect에 key값이 들어가지 않았을 때의 동작과 같다. 또한 SideEffect로 수행하는 Effect는 Composable이 Dispose될 때 정리가 불가능하다.

 

따라서 SideEffect는 쉽게 사용될 수 있지만, LaunchedEffect와 DisposableEffect의 열화 버전이라 key 값에 따라 제어하거나 Composable이 Dispose될 때 정리되어야 하는 Effect에는 사용하기가 어렵다. 

 

결론은 SideEffect는 LaunchedEffect나 DisposableEffect로 충분히 대체 가능하니 이 둘을 사용하도록 하자.

 

참조

1. LaunchedEffect : https://kotlinworld.com/246

2. DisposableEffect : https://kotlinworld.com/257

반응형
저작자표시 비영리 변경금지
  1. SideEffect란
  2. SideEffect의 사용
  3. SideEffect의 한계점
  4. 참조


 

Kotlin, Android, Spring 사용자 오픈 카톡

오셔서 궁금한 점을 질문해보세요!
비밀번호 : kotlin22

open.kakao.com

'Android Jetpack Compose/Compose Side Effect' 카테고리의 다른 글
  • [Compose Side Effect] Disposable Effect 란 무엇인가?
  • [Compose Side Effect] LaunchedEffect에서 한 번만 실행되어야 하는 동작 처리하기
  • [Compose Side Effect] rememberCoroutineScope을 이용해 Composable의 생명주기에 맞춰 코루틴 수행하기
  • [Compose Side Effect] 1. LaunchedEffect 를 이용한 suspend fun 실행
Dev.Cho
Dev.Cho
'조세영의 Kotlin World'는 Kotlin를 전문적으로 다루는 개인 기술 블로그입니다. Kotlin 세계에 대한 양질의 자료를 제공하며 Kotlin, Android, Spring, CI, CD 분야에 대해 다룹니다.
Dev.Cho
조세영의 Kotlin World
Dev.Cho
전체
오늘
어제

블로그 메뉴

  • LinkedIn
  • GitHub
  • 분류 전체보기 (491)
    • Kotlin (104)
      • Class and Interface (19)
      • Variable and Function (8)
      • Modifier (5)
      • Collection (14)
      • Time (8)
      • 동시성 제어 (7)
      • Reactive Programming (2)
      • Paradigm (2)
      • Kotlin 자료구조 (15)
      • Design Patterns (11)
      • Algorithm (3)
      • Exception (1)
      • 기타 (9)
      • Update History (0)
    • Coroutines (32)
      • Coroutine Basics (18)
      • Flow (9)
      • CoroutineScope (3)
      • Debugging (2)
    • Testing Codes (28)
      • Test 기본 (3)
      • JUnit5 (9)
      • MockK (6)
      • Testing Coroutines (1)
      • Testing Android (8)
      • Test 기타 (1)
    • Spring (50)
      • Dependency Injection (18)
      • Settings (5)
      • REST API (0)
      • DevTools (1)
      • MVC (18)
      • Error (2)
      • MongoDB (2)
      • Database (4)
    • Android (39)
      • Architecture (2)
      • Component (5)
      • Manifest (1)
      • Lifecycle (2)
      • Dependency Injection (17)
      • Resource (1)
      • Storage (1)
      • Security and Optimization (1)
      • WebView (2)
      • Error (6)
    • Android Jetpack Compose (33)
      • Compose (6)
      • Compose Modifier (13)
      • Compose Resource (4)
      • Compose State (4)
      • Compose Side Effect (6)
    • Android Jetpack Compose UI (48)
      • Compose Layout (14)
      • Compose Text (10)
      • Compose Button (5)
      • Compose Dialog (2)
      • Compose TextField (0)
      • Compose UIs (4)
      • Compose Animation (1)
      • Compose Canvas (12)
    • Android Jetpack (10)
      • Datastore (5)
      • ViewModel (4)
      • LiveData (1)
      • Paging (0)
    • KMP (5)
    • Programming (4)
    • Machine (9)
      • JVM (7)
      • Linux (2)
    • CI, CD (74)
      • Gradle (12)
      • Groovy Gradle (5)
      • Git (25)
      • Git Remote (5)
      • GitHub (5)
      • GitHub Actions (21)
    • Network (33)
      • GraphQL (12)
      • HTTP (11)
      • Basic (9)
    • 오픈소스 (3)
    • Database (3)
      • MongoDB (3)
    • IDE (6)
      • Android Studio (2)
      • Intellij (4)
    • Firebase (1)
    • Javascript (9)

공지사항

  • 코틀린 코루틴 완전 정복 강의 in 인프런 오픈
  • 코틀린 코루틴의 정석 책 출간
  • Kotlin Coroutines 공식 기술 문서 한국어 번⋯
  • GitHub에서 조세영의 Kotlin World를 Foll⋯
  • 문의&제안

인기 글

태그

  • Unit Testing
  • github actions
  • GraphQL
  • Spring
  • junit4
  • junit
  • 코틀린
  • flow
  • Class
  • dagger2
  • kotlin spring
  • HTTP
  • Android
  • 유닛 테스팅
  • compose
  • 코루틴
  • Spring boot
  • 안드로이드
  • junit5
  • Kotlin
  • GIT
  • github
  • Coroutine
  • 의존성 주입
  • gradle
  • 스프링
  • java
  • Jetpack Compose
  • Dependency Injection
  • Android Compose

최근 글

반응형
hELLO · Designed By 정상우.v4.3.0
Dev.Cho
[Compose SideEffect] SideEffect를 이용해 부수효과 처리하기
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.