
컴포저블은 컴포지션을 시작하고 0회 이상 재구성되며 컴포지션을 종료하게 되는데 일반적으로 State<T> 객체가 변경되면 트리거 된다. 컴포지션에서 특정 State<T>를 읽는 모든 컴포저블 및 호출하는 컴포저블 중 건너뛸 수 없는 모든 컴포저블을 실행하게 된다.
컴포저블에는 부수효과가 없어야 하는데 앱 상태를 변경하려는 경우 부수효과가 있어야 한다면 예측 가능한 방식으로 실행되도록 Effect API를 사용해야 한다. 관련 Android Developer 문서
컴포지션 내 컴포저블의 인스턴스는 호출 사이트(컴포저블이 호출되는 코드 위치)로 식별되며
리컴포지션 시 컴포저블이 이전 컴포지션 시 호출한 것과 다른 컴포저블을 호출하는 경우 Compose는 호출되거나 호출되지 않은 컴포저블을 식별하며 두 컴포지션 모두에서 호출된 컴포저블의 경우 입력이 변경되지 않은 경우 재구성하지 않는다.
@Composable
fun LoginScreen(showError: Boolean) {
if (showError) {
LoginError()
}
LoginInput() // This call site affects where LoginInput is placed in Composition
}
@Composable
fun LoginInput() { /* ... */ }
@Composable
fun LoginError() { /* ... */ }
따라서 위의 함수에서 상태 변경에 따라 LoginError()가 실행되게 되는데 여기서 인스턴스가 새로 만들어지는 건 LoginError뿐이다.

컴포저블을 여러 번 호출하면 컴포저블이 컴포지션에도 여러 번 추가되는데 동일한 호출사이트에서 여러 번 호출하는 경우 Compose가 각 컴포저블 호출을 고유하게 식별할 수 있는 정보가 생기지 않는다. 따라서 호출 사이트 외의 실행 순서가 사용되며 경우에 따라 원치않는 동작이 발생할 수 있는데 지금부터 알아보자
1. 단순 반복으로 하단에 컴포저블 추가
@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)
}
}
}
위의 코드에서는 컴포저블을 단순히 반복해서 추가하는 형태(맨 아래에)이다. 따라서 새롭게 추가해봤자 재구성되는(인스턴스가 새로 생성되는) MovieOverview는 단 하나다.

하지만 만약, 순서를 재정렬 하거나 목록의 상단, 혹은 가운데에 항목을 추가하게 된다면 호출 사이트 외의 실행 순서에 모든 컴포저블이 영향이 가게 된다. 그렇게되면 아래와 같이 모든 컴포저블이 재구성(인스턴스 새로 생성)되고 이는 성능 저하로 이어지게 된다.
2. 기존 컴포저블의 위치값이 변경
@Composable
fun MovieOverview(movie: Movie) {
Column {
// Side effect explained later in the docs. If MovieOverview
// recomposes, while fetching the image is in progress,
// it is cancelled and restarted.
val image = loadNetworkImage(movie.url)
MovieHeader(image)
/* ... */
}
}
해당 코드는 MovieHeader 컴포저블을 최상단에 새롭게 넣어주고 있는데 이때 리컴포지션이 발생하면 하단에 있는 모든 컴포저블의 위치가 변하게 되므로 재사용 할 수 없게되며 모든 부수효과(side effect)가 다시 시작된다. 컴포저블은 모두 재구성되게 된다.

🍕 호출 사이트: 컴포저블이 호출되는 소스코드 위치로 컴포지션 내 위치와 UI트리에 영향을 미친다. 즉, 같은 컴포저블 함수가 여러 번 호출되더라도 각 호출 위치마다 독립된 인스턴스로 Compose에 의해 관리 된다는 뜻
🍔 부수효과(Side Effect): 컴포저블 외부에 영향을 미치는 동작으로 일반적으로 외부 자원에 접근하거나, UI와 직접적으로 관련되지 않은 작업을 뜻한다.