[compose] Re-Composition이 언제 일어나는가

sundays·2023년 3월 24일
0

jetpackcompose

목록 보기
5/7

Re-Composition은 단순히 데이터의 상태 변경 으로 일어나는 부분은 아닙니다. 상태 변경이 되었다고 하더라도 compose가 이를 찾아내지 못하는 경우도 존재하며 상태 변경이 이루어지지 않았다고 생각했는데도 일어나기도 합니다. 이것은 단순히 개발자 관점이며 Compose에서 Re-composition이 맞는지 인지하는 방법이 존재 합니다.

Composable 수명 주기

  1. Composition 시작
  2. Composition내 Composable은 Re-composition이 0회 이상 재구성
  3. Composition 종료

앱 상태가 변경되면 Re-Composition을 예약후 상태 변경에 따라 변경될 수 있는 Composable을 다시 실행해서 변경사항을 반영합니다.

리컴포지션은 일반적으로 State<T> 객체가 변경이 될때 트리거 됩니다 Compose에서는 이러한 객체들을 추적하고 컴포지션에서 특정 State<T>를 읽는 모든 컴포저블 및 호출하는 컴포저블 중 건너뛸 수 없는 모든 컴포저블을 실행합니다.

만약 composable이 외부에 있는 상태를 변경하거나 접근해야 할 경우에는 반드시 effect를 사용해야 합니다. effects는 UI를 emit하지 않고 side-effect를 처리하기 위해 composition이 완료가 되면 수행되는 composable function 입니다

Composable instance

@Composable
fun MyComposable() {
    Column {
        Text("Hello")
        Text("World")
    }
}

MyComposable()이 여러개의 composable을 갖고 해당 instance들이 composition에 배치되어 각각의 lifecycle이 생성됩니다.

각각의 instance가 따로 존재함을 나타내기 위해서 composable이 색상이 다릅니다.

Re-composition이 발생되지 않는다면?

  • 일단 UI가 변동되지 않는다
  • 만약 리스트를 넣었는데 항상 같은 리스트인데도 Re-composition이 발생하면 Main Thread의 사용량이 많아 지면 성능저하가 발생할 수 있다

그러므로 변동된 항목 만 Re-composition 되게 해야 한다는 말이다

Re-composition이 발생되지 않는 경우

  • 동일한 Call site(동일한 소스코드 위치)에서 이전에 이미 호출 된 적이있다
    • composition 에 추가 되어있다
    • 화면이 이미 그려진 상태
  • Param이 Stable Type 이다
  • Stable Type의 Param이 변경되지 않았다

Stable Type

composable이 있는 경우 모든 입력이 안정적이고 변경되지 않은 Stable Type은 다음과 같은 CASE를 모두 만족해야 합니다.

  • 두 instance의 equals() 결과 비교가 항상 동일
  • 공개된 속성의 Type이 변경되면 Composition이 변경된 알림이 전송되는 경우
    • param 으로 넘겨 받는 Type T는 State<T> 로 정의된 상태
  • 공개된 속성 역시 모두 다 stable하다
    • State<T> 에서 T역시 stable 해야한다

이 항목들은 Immutable 하기 때문에 stable Type 이라고 간주될 수 있습니다.

  • @Stable Annotation
  • 모든 Primptive type : Boolean, Long, Float, Char, Int
  • Strings
  • Lambda

예외로 Mutable한 MutableState 또한 Stable 로 간주됩니다. 왜냐하면 State의 .value 속성이 변경되면 Compose에 알림이 전송되기 때문입니다.

Re-composition의 예시

1. 조건부로 호출

composable에서 if-else와 같은 조건문으로 LoginError()를 실행시키는 경우

@Composable
fun LoginScreen(showError: Boolean) {
    if (showError) {
        LoginError()
    }
    LoginInput() // This call site affects where LoginInput is placed in Composition
}

@Composable
fun LoginInput() { /* ... */ }

showError = true가 되어도 LoginInput() 은 재사용 가능하고 LoginError()는 조건부로 호출 되었습니다

2. 리스트 행 추가

리스트의 항목이 추가되면 Recomposition이 발생됩니다.

@Composable
fun MoviesScreen(movies: List<Movie>) {
    Column {
        for (movie in movies) {
            // MovieOverview composables are placed in Composition given its
            // index position in the for loop
            MovieOverview(movie)
        }
    }
}

2.1. 리스트에 행이 추가 될때(가장 상위에 위치에 추가가 된 경우)

  • 모든 MovieOverview() 가 Re-composition 되었습니다 (재사용 불가)

2.2. 리스트에 행이 추가 될때(가자 하위에 위치에 추가가 된 경우)

  • Composition의 MovieOverview()은 재사용 할 수 있습니다. 색상이 동일하면 Re-composition 되지 않은 것입니다.

Re-composition 방지

위의 경우처럼 리스트를 재정렬하는 경우에 MovieOverview Composable을 Re-composition 하는 대신에 인스턴스를 재정렬하였다고 인지하게 해주는게 좋은데 Compose에서 런타임에 트리에 특정 부분인 Key Composable 를 추가하여 이 instance를 식별하는데 사용할 값을 지정해주면 됩니다

@Composable
fun MoviesScreen(movies: List<Movie>) {
    Column {
        for (movie in movies) {
            key(movie.id) { // Unique ID for this movie
                MovieOverview(movie)
            }
        }
    }
}

이 key는 Composition의 instance가 식별되는데 사용됩니다. 이 Key는 전체적으로 Unique하지 않아도 되고 Call Site의 Composable 에서만 고유하면 됩니다. 따라서 이 예시에서 각 movie의 고유한 key가 존재해야합니다. key를 다른 composable과 공유하는 것은 상관없습니다.

목록의 요소가 변경되었다고 해도 MovieOverview 컴포저블 고유 키를 가지고 MovieOverview instance들을 인식해서 재 사용할 수 있습니다.

lazyColumn

lazyColumn 의 경우에는 key Composable 지원 기능이 내장되어 있습니다.

@Composable
fun MoviesScreen(movies: List<Movie>) {
    LazyColumn {
        items(movies, key = { movie -> movie.id }) { movie ->
            MovieOverview(movie)
        }
    }
}

결론

Re-composition은 UI가 변경되어야 할때 발생되는데, Stable Type이 아닌경우와 instance들이 동일하지 않을때 stable type으로 입력된 파라미터가 동일하지 않을때 발생되고 있습니다. 그리고 만약 리스트에서 Re-composition이 발생되지 않게 하려면 Call Site의 key값으로 composable의 instance들의 id를 부여해서 값을 식별하게 하여 re-composition이 발생되지 않게 됩니다.

Reference

profile
develop life

0개의 댓글