Compose Stability Analyzer 0.8.0: Stability Reality Check와 Recomposition Blame
Compose Stability Analyzer 0.8.0: Stability Reality Check와 Recomposition Blame
Jetpack Compose의 강한 스키핑(strong skipping) 모드는 안정성(stability)이 리컴포지션(Recomposition)에 영향을 주는 방식을 바꿔 놓았습니다. 강한 스키핑이 도입되기 전에는 불안정한 매개변수가 단 하나만 있어도 컴포저블(composable)이 스키핑 불가능 상태가 되었습니다. 강한 스키핑에서는 불안정한 매개변수를 인스턴스 동일성(instance identity, ===)으로 비교하고 안정적인 매개변수는 equals()로 비교합니다. 따라서 불안정한 매개변수를 가진 컴포저블이라도 동일한 인스턴스가 다시 전달되기만 하면 스키핑될 수 있습니다. 그 결과 컴파일러가 붙인 "불안정(unstable)"이라는 꼬리표는 더 이상 "리컴포지션될 것"과 깔끔하게 맞아떨어지지 않게 되었고, 정적 분석이 예측하는 바와 런타임에서 실제로 벌어지는 일 사이의 간극은 더 벌어졌습니다.
Compose Stability Analyzer는 거터 아이콘(gutter icon), 호버 툴팁(hover tooltip), 인라인 힌트(inline hint), Stability Explorer, Recomposition Cascade, Live Recomposition Heatmap을 통해 안정성 정보를 Android Studio 안에서 직접 보여 주어 왔습니다. 이 기능들은 "이 컴포저블은 안정적인가?", "얼마나 자주 리컴포지션되는가?"라는 질문에 답합니다. 0.8.0 버전은 예측과 현실 사이에 놓인 두 가지 질문에 답합니다. 바로 "안정성 예측이 런타임에서 실제로 들어맞았는가?"와 "애초에 이 리컴포지션을 무엇이 유발했는가?"입니다.
이 글에서는 0.8.0 버전에 도입된 두 가지 새로운 기능, Stability Reality Check와 Recomposition Blame을 살펴봅니다.
Stability Reality Check
컴파일러는 컴파일 타임에 안정성을 예측하지만, 강한 스키핑은 런타임에 인스턴스 동일성을 사용하여 스키핑 여부를 결정합니다. 컴파일러가 불안정하다고 표시한 매개변수라도, 동일한 인스턴스가 계속 다시 전달되는 덕분에 문제없이 스키핑되는 경우가 많습니다. 반면 또 다른 매개변수는 내용이 동일한데도 매번 새로운 인스턴스가 도착하는 탓에 프레임마다 조용히 리컴포지션됩니다. Stability Reality Check는 이 정적 예측을 히트맵 스트림에서 들어오는 실시간 리컴포지션 데이터와 결합하여, 실제 기기에서 무슨 일이 벌어지는지를 기준으로 각 매개변수에 등급을 매깁니다.

각 매개변수는 다음 네 가지 등급 중 하나를 받습니다.
- Confirmed (확인): 예측이 들어맞은 경우입니다. 매개변수가 안정적이며 불필요하게 리컴포지션되지 않습니다.
- False alarm (거짓 경보): 컴파일러는 불안정하다고 표시했지만, 런타임에서는 인스턴스가 참조적으로 안정적이어서 Compose가 스키핑합니다. 이 경고는 무시하셔도 됩니다.
- Silent waste (조용한 낭비): 값은
equals기준으로 동일하지만 리컴포지션마다 새로운 인스턴스로 도착하므로, 강한 스키핑의===검사가 실패하고, 스키핑될 수 있었던 컴포저블이 결국 리컴포지션됩니다. - Justified (정당함): 값이 리컴포지션 사이에 실제로 달라지므로, 리컴포지션이 반드시 필요한 경우입니다.
등급은 세 곳에 표시됩니다. 각 컴포저블 위에 나타나는 에디터 인레이(inlay), 예측값과 실제값을 나란히 보여 주는 호버 툴팁, 그리고 관찰된 모든 컴포저블을 낭비된 리컴포지션 횟수와 함께 나열하는 툴 윈도우의 전용 Reality 탭입니다. 등급을 확인하시려면, 등급을 매기고 싶은 컴포저블에 @TraceRecomposition을 붙이고, 히트맵을 시작한 뒤, 앱을 직접 조작해 보시면 됩니다. 등급 산정은 관찰된 리컴포지션에 의존하므로, 매개변수는 해당 컴포저블이 몇 차례 리컴포지션된 뒤에야 등급을 받습니다. 단순히 스키핑되기만 하는 안정적인 매개변수는 예상대로 Confirmed로 표시되며, 별도로 신경 쓰실 필요가 없습니다.
이 가운데 실제로 조치를 취할 가치가 있는 등급은 Silent waste입니다. 이 등급은 값은 그대로인데 동일성만 바뀌어 강한 스키핑을 무력화하는 매개변수를 가리키며, 대개 리컴포지션마다 인라인으로 생성되는 리스트나 람다(lambda), 객체가 그 원인입니다. 값을 remember로 호이스팅(hoisting)하거나 타입을 안정적으로 만들면 이 낭비가 사라집니다. Reality Check 문서에서 각 등급을 더 깊이 있게 다룹니다.
Recomposition Blame
컴포저블이 너무 자주 리컴포지션된다는 사실을 아는 것은 절반에 불과합니다. 나머지 절반은 '왜'입니다. Recomposition Blame은 리컴포지션을 그 원인까지 두 가지 방식으로 거슬러 추적합니다.
첫 번째는 런타임에 포착되는 상태 쓰기 지점(state write site)입니다. 컴포저블에 @TraceRecomposition(traceStates = true)를 붙이면, 플러그인이 Compose Snapshot 쓰기 옵저버를 설치합니다. 이 옵저버는 각 내부 상태가 어디에서 변경되었는지를 기록하여 로그에 덧붙입니다.
[state] counter: Int changed (0 → 1) ← onClick (MainActivity.kt:97)
← method (File.kt:line) 접미사는 리컴포지션을 유발한 상태를 어느 줄에서 썼는지 정확히 가리키므로, 추측에 불과하던 것을 정밀한 포인터로 바꿉니다. 쓰기 지점 포착은 Android와 JVM에서 var x by remember { mutableStateOf(...) } 형태로 선언된 위임 스칼라 상태(delegated scalar state)에 대해 동작하며, 동일한 접미사가 히트맵 인레이의 툴팁에도 나타납니다.
두 번째는 정적으로 추적되는 매개변수 출처(parameter provenance)입니다. 에디터에서 아무 @Composable이나 마우스 오른쪽 버튼으로 클릭한 뒤 "Blame this Recomposition"을 선택하시면 됩니다.

그러면 Blame 탭이 열리고, Cascade를 거꾸로 뒤집은 모습이 나타납니다. 즉, 어떤 컴포저블들이 이 컴포저블을 호출하는지, 그리고 각 인자의 값이 어디에서 비롯되는지를 보여 줍니다.

각 호출자 노드는 자신이 전달하는 인자들을 나열하고, 각 값의 정적 출처를 함께 표시합니다. 그 출처는 val이나 var 프로퍼티, 전달받은 매개변수, 함수 호출, 또는 동적 표현식 등입니다. 이 뷰는 실행 중인 앱이 필요 없으므로, 코드를 읽으면서 불안정한 입력이 어디에서 오는지 추적하실 수 있습니다. 노드를 더블 클릭하면 해당 소스로 이동합니다. Recomposition Blame 문서에서 두 계층과 그 한계를 모두 설명합니다.
두 기능을 함께 사용하기
이 두 기능은 플러그인의 나머지 기능들과 하나의 고리를 완성합니다. 정적 분석과 Cascade는 어떤 컴포저블이 리컴포지션될 수 있고 무엇에 영향을 미칠지 보여 줍니다. 히트맵은 그중 어떤 것이 실제로, 또 얼마나 자주 리컴포지션되는지 짚어 냅니다. Stability Reality Check는 각 리컴포지션이 정당한지 아니면 낭비인지 판정하며, Recomposition Blame은 그 원인이 무엇인지 밝혀 줍니다.
실제 작업 흐름은 다음과 같습니다. 먼저 히트맵을 시작하고 앱을 직접 조작해 보십시오. 어떤 컴포저블이 Silent waste 등급을 보이면, Blame을 열어 어떤 호출자가 문제의 인자를 넘기는지, 또 그 값이 어디에서 오는지 찾아보거나, 상태 쓰기 지점을 읽고 어느 줄이 그 리컴포지션을 유발한 상태를 변경했는지 확인하시면 됩니다. 그런 다음 값을 remember로 호이스팅하거나, 타입을 안정적으로 만들거나, 상태 쓰기를 다른 곳으로 옮겨 근본 원인을 바로잡습니다. 그러고 나면 등급이 Silent waste에서 Confirmed로 바뀌는 모습을 확인하실 수 있습니다. 이제 예측과 런타임 동작, 그리고 그 원인이 서로 일치하게 됩니다.
두 기능 모두 Compose Stability Analyzer 0.8.0 버전에서 사용할 수 있으며, JetBrains Marketplace에서 설치하시거나
저장소에서 직접 빌드하실 수 있습니다.
문서에서 모든 기능을 자세히 다룹니다.

