Composable 함수의 의미

Composable 함수는 Jetpack Compose의 가장 기본을 이루고, Composable 트리 구조를 작성하는 데 사용된다.
여기서 표현한 트리의 의미는 Compose Runtime이 Composable 함수를 메모리상에서 하나의 노드로서 여긴다는 뜻이다.

모든 코틀린 함수는 @Composable 어노테이션을 통해 Composable함수로 만들 수 있다.

@Composable  
fun Badge(lable: String) {  
//Composable code  
}

@Composable 어노테이션을 사용하여 Compiler에게 이 함수는 본질적으로 데이터를 하나의 노드로 변환하여 위에서 말한 Composable 트리에 등록하겠다는 의도를 전달할 수 있다.

즉 우리가 Composable 함수를 @Composable (Input) -> Unit과 같이 입력값은 데이터가 되고, 출력은 함수의 반환 값이 아닌, 트리에 요소를 삽입한 일련의 동작이라고 볼 수 있다.

이러한 동작을 함수 실행의 부수 효과(Side Effect)로 발생한다고 말할 수 있다.

Composable 함수를 실행하는 유일한 목적은 트리의 인메모리 표현을 만들거나 업데이트를 하기 위함이다.
Composable 함수는 읽은 데이터가 변경될 때마다 다시 실행되므로 실제로 화면에 표현되는 데이터의 메모리를 항상 최신 데이터로 유지한다.

Composable 함수의 속성

함수에 @Composable 어노테이션을 추가하는 것은 아래와 같은 특징을 부여한다.

  1. 해당 어노테이션이 적용된 함수나 표현식의 타입을 변경
  2. 해당 타입에 대한 일부 제약 사항이나 특성을 부여

이러한 특징들로 인해 Jetpack Compose 와 고도의 밀접한 관련을 가지며 Compose의 라이브러리들이 각자의 기능을 하는데 큰 역할을 한다.

Compose Runtime은 Composable 함수가 사전에 위와 같은 특징을 가지도록 강제하기 때문에 이를 근거로 병렬 Composition, 우선순위 Composition 정렬, Relayout Composition(Composition을 재실행하여 레이아웃을 업데이트하는 과정), 위치 기억법과 같은 다양한 런타임 최적화 기법을 포함한다.
일반적ㅇ로 런타임 최적화는 코드에 대하 어느 정도 확실성을 보장 할 수 있을 때만 가능하므로, 런타임은 사전 조건이나 특징을 추론할 수 있다.

호출 컨텍스트

Compose Compiler는 코틀린 컴파일러 프러그인으로 일반적으로 컴파일러 실행단계에서 동작한다.
따라서 Kotlin 컴파일러가 액세스 할 수 있는 모든 정보에 접근할 수 있고, 이러한 특성을 통해 Composable로 작성된 원본 소스를 컴파일러가 소스파일 해석을 시작할 때 Composable 함수에 추가적인 정보를 부여할 수 있다.
추가적인 정보란 각 Composable 함수의 매개변수 목록의 끝에 Composer을 추가한다.
이 매개변수는 암묵적이고, 개발자는 알아야 할 필요는 없는 비가시적인 매개변수지만, Composer 매개변수의 인스턴스는 런타임에 주입되며, 모든 하위 Composable 호출로 전달되므로 트리의 모든 수준에서 접근 가능하다.

아래와 같은 일반적인 Composable 함수가 있다.

@Composable  
fun Badge(label: String) {  
Column(modifier = Modifier.padding(16.dp)) {  
        Text(text = label)  
    }  
}

Compose Compiler는 위의 코드를 아래와 같이 Compoer를 포함하여 변환한다.

fun Badge(label: String, $composer: Composer<\*>) {  
Comlumn(modifier = Modifier.padding(16.dp), $Composer) {  
        Text(  
            text = label,  
            $composer
        )  
    }  
}

이 코드에서 알 수 있듯이 Composer는 트리 내에서 모든 Composable 호출로 전달된다.
또한 Compose Compiler는 Composable 함수에 규칙을 적용하여 Composable함수는 오직 다른 Composable 함수 내에서만 호출할 수 있다.
이러한 호출 컨텍스트는(composer) 필수적인 요소이며, 이는 트리가 오직 Composable 함수들로만 구성되고, Composer가 트리를 따라 하향 전달될 수 있도록 하기 위함이다.

Composer는 개발자가 작성한 Composable 코드와 Compose Runtime 간의 인터페이스 역할을 한다.
Composable 함수는 Composer를 통해 트리에 변경 사항을 전달하고, 런타임시에 트리의 형태를 빌드하거나 업데이트한다.

멱등성

먼저 멱등성이란 무엇인지 알고 가자.
멱등성은 연산을 여러 번 수행하더라도 결과가 달라지지 않는 불가변의 성질을 의미한다.
즉 연산을 여러 번 반복하더라도 한 번만 수행된 것과 같은 성질을 말한다.

Jetpack Compose Runtime은 특정 시점과 몇 가지의 근거를 통해 Composable 함수를 recomposition 하는 역할을 한다.

Recomposition이란 입력값이 변경될 때마다 Composable 함수를 다시 실행하여 업데이트된 정보를 방출하고 트리를 업데이트하는 작업을 의미한다.
Recomposition의 과정은 트리를 아래로 순회하면서 어떤 노드를 재구성해야 하는지 체크하고, 이 과정에서 입력값이 변경된 노드만 recomposition을 수행하고 나머지는 생략한다.

나머지를 생략할 수 있는 근거는 해당 노드를 대표하는 Composable 함수가 멱등성의 성질을 가진다고 가정하기 때문이다.
따라서 동일한 입력값에 대한 결과는 이미 메모리에 적재되어 있으므로 Compose는 다시 실행할 필요가 없고, 생략을 통해 부하를 줄일 수 있다.

+ Recent posts