Compose를 이용하면 component로 custom view를 자유자재로 만들 수 있다. material component를 재정의해서 쓸 수도 있으며, 아예 처음부터 만들 수도 있다. 이런 자유로운 뷰 구성이 Compose의 가장 큰 장점이라고 체감한다.
RadioButton 처럼 하나만 클릭되는 버튼들을 커스텀으로 제작하는 중 마주한 오류가 있다. 분명히 클릭 로그는 찍히는데, UI상으로 변화가 없다..!
이는 컴포넌트의 최상단이 Box가 아닌 Surface로 지정해서 생긴 문제였다. 좀 더 자세히 알아보자.
Surface의 내부 최적화에 따른 recomposition 누락Compose의 Surface는 내부적으로 색상, elevation, 클릭 효과 등을 처리하면서 최적화를 수행한다.
이 과정에서 상태 변화가 있더라도 UI에 변화가 없다고 판단되면 draw를 생략하여 화면이 갱신되지 않을 수 있다.
Compose에서 Surface와 Box는 모두 UI를 구성하는 레이아웃 요소지만, 내부 작동 방식과 recomposition 최적화 처리에서 큰 차이를 보인다. 이번 사례에서 Surface는 상태 변화에 따라 UI가 눈에 보이게 변하지 않았으며, 그 이유는 아래와 같다.
Surface는 내부적으로 머티리얼 레이어 계층, 색상, 그림자, 클릭 등을 종합적으로 처리한다. 이 과정에서 재조합 최적화가 발생하여, 상태 변화가 시각적으로 드러나지 않을 수 있다.
반면, Box는 단순한 컨테이너로 동작하므로 상태 변화가 곧바로 시각적 변화로 이어진다.
Surface는 단순한 박스가 아니라 다음과 같은 기능을 자동으로 포함한다.
| 기능 | 설명 | 
|---|---|
| 색상 블렌딩 | elevation, background color에 따라 색상이 자동으로 섞인다 | 
| 그림자 처리 | 머티리얼 스타일의 그림자(elevation)를 렌더링한다 | 
| Ripple 효과 | 클릭 시 Ripple 효과를 기본으로 제공한다 | 
| ContentColor 제공 | 내부의 Text, Icon 등에 LocalContentColor를 주입한다 | 
| CompositionLocal 변경 | 내부적으로 LocalContentColor, LocalAbsoluteTonalElevation 등의 context 값을 변경한다 | 
Box는 단순한 layout container로, 다음과 같은 특징이 있다.
Surface(
  color = if (isSelected) X else Y,
  border = ...
)
이 경우, color, border, shape, tonalElevation 등이 종합적으로 처리된다. 이 중 draw layer가 동일하다고 판단되면 Compose는 화면을 다시 그리지 않는다.
따라서 로그는 나오지만 실제 화면에는 아무런 변화도 보이지 않게 된다.
Surface(
    color = if (isSelected) Color.Red else Color.Blue, // 확실한 색상 차이
    border = BorderStroke(2.dp, Color.Magenta), // 극단적인 변화
    modifier = Modifier.clickable { onSelect() }
) {
    Text(text = "테스트", modifier = Modifier.padding(20.dp))
}
이렇게 설정했음에도 UI가 변하지 않는다면, 이는 Surface 내부의 최적화 때문이라고 볼 수 있다.
| 항목 | Surface | Box | 
|---|---|---|
| 내부 최적화 | 많음 (색상, Elevation, Context 등) | 거의 없음 | 
| Draw 생략 가능성 | 높음 | 낮음 | 
| 직접 제어 | 추상화되어 있음 | 명확하게 제어 가능 | 
| 상태 변화 반영 | 무시될 가능성 있음 | 즉시 반영됨 | 
Surface는 복잡한 머티리얼 레이어를 제공하므로, 간단한 상태 기반 UI 커스터마이징에는 Box와 Modifier 조합이 더 안정적이다.
특히 색상이나 border로만 상태를 표현하는 경우, Surface의 내부 최적화로 인해 오히려 recomposition 누락 문제가 발생할 수 있다.
UI 컴포넌트를 만들 때는 Surface 말고 Box를 사용하자!!!! 리컴포지션 해야한다!!! 
결론: Surface는 상태 변화가 있어도 재조합을 생략하는 경우가 많기 때문에, 상태 기반 UI 컴포넌트에는 적합하지 않다.