Jetpack Compose(1) - 이해

김재원·2022년 3월 12일
0

JetpackCompose

목록 보기
1/9
post-thumbnail

이 글은 Jetpack Compose 공식문서 - Compose 이해를 읽어보면서 정리한 글 입니다.

Jetpack Compose는 Android를 위한 현대적인 선언형 UI 도구 키트입니다.

compose가 Ui를 구현할 때 매우 편리하다는 소문을 들었습니다. 그래서 지금부터 compose는 어떤것이고, 어떻게 쓰는것이며, 왜 좋은지에 대해 Jetpack Compose 공식문서를 보며 정리해보려고 합니다😄.

선언형 프로그래밍

Jetpack Compose는 UI를 선언형 프로그래밍으로 구현합니다.

Compose가 나오기전, 안드로이드 UI를 만들때는 xml파일에 Ui를 구현하고, findViewById()와 같은 함수로 View를 찾아 setText(), addChild()등의 함수를 사용하여 View를 업데이트 하였었습니다. 이런 방식은 명령형 프로그래밍이라고 합니다.

명령형 프로그래밍은 다음과 같은 문제점들이 있었습니다.

  • 뷰를 수동으로 조작하기 때문에 오류가 발생하기 쉬었습니다.
  • 뷰를 업데이트 하는 명령이 충돌하면 예측하지 못한 뷰의 상태가 나타나기도 했습니다.
  • 업데이트 해야하는 뷰의 수가 많을수록 유지보수가 까다로웠습니다.

    옛날에 제가 BMI수치를 계산해주는 앱을 만들었을 당시 명령형 프로그래밍으로 구현했던 코드입니다🙃.

이러한 문제점들을 개선하기 위해 개발자들은 선언형 UI 모델로 전환하기 시작하였습니다. 이 방법은 화면 전체를 재생성한 뒤, 필요한 변경사항만 적용하는 방식으로 작동합니다. 화면 전체를 재생성하는데에는 시간, 컴퓨팅 성능 및 배터리 사용량의 비용이 많이 들기 때문에 Compose는 지능적으로 필요한 부분만 다시 그리게 됩니다.

Composable 함수

BMI수치를 표현하는 Text를 Compose로 구현하면 이렇게 될 수 있을것 같습니다😆.

Compose는 Composable함수를 통해 UI를 구현할 수 있습니다. Composable함수는 다음과 같은 특징을 갖고있습니다.

  • @Composable 어노테이션이 있어야합니다.
    이 어노테이션을 통해 Compose에게 UI를 만드는 함수임을 알립니다.
    (+ Composable함수는 첫글자가 대문자여야합니다.)
  • 매개변수를 통해 데이터를 받습니다.
    전달받은 데이터를 사용하여 화면에 표시합니다.
  • 아무것도 반환하지 않습니다. Composable함수를 UI를 구성하므로 아무값도 반환할 필요가 없습니다.
  • Composable함수는 빠르고, 부수효과가 없습니다.
    • 여러번 호출해도 동일하게 작동합니다.
    • 전역변수나 random()값을 사용하지 않습니다.

선언형 패러다임 전환

xml파일로 UI를 구현할 때, 각 View들은 상호작용할 수 있는 getter 및 setter을 제공했습니다. 하지만 Compose에서는 getter와 setter를 제공하지 않습니다. Compose에서는 Composable함수를 다른 인수를 넣어 호출함으로써 UI를 업데이트 합니다.

위 코드에서 View를 업데이트 하고싶다면 data를 변경하여 호출하면 View가 변경됩니다.

반대로, 사용자가 View를 클릭하여 이벤트를 발생시키면, 앱 로직에 전달하여 상태를 변경시킵니다. 이렇게 되면 상태가 변경되었으므로, UI요소가 다시 그려집니다. 이 과정을 재구성이라고 합니다. 재구성은 아래에서 좀 더 자세히 다뤄보도록 하겠습니다.

View를 클릭했을 때 호출할 함수를 매개변수로 전달 합니다. 최상위 Composable함수에서는 클릭했을때 상태를 어떻게 변경할 것인지 선언해두었습니다.

동적 콘텐츠

Composable함수는 Kotlin으로 작성하기 때문에 동적으로 개발할 수 있습니다. 예를들어 사용자 이름들을 받아 환영하는 UI를 만든다고 하면 다음과 같이 만들 수 있습니다.

사용자의 수에 따라 Text의 수가 변경되는 동적 UI를 만들었습니다😮.

이렇게 Kotlin으로 작성할 수 있다는 점은 동적 콘텐츠를 만들 수 있다는 점 외에도 Kotlin의 유연성을 완전 활용할 수 있다는 점에서도 장점입니다.

재구성(Recomposition)

상태가 업데이트 되고, 그에따라 UI가 다시 그려지는 과정을 재구성이라고 합니다. Compose에서 재구성을 할때는 업데이트된 상태에 종속되어 있는 Composable함수만 재구성 합니다. 이를 통해 컴퓨터 성능 및 배터리 성능을 최적화 시킵니다.

매개변수가 변경되지 않은 Composable함수는 모두 건너뜀으로 부작용에 주의해야합니다. 대표적인 부작용은 다음과 같습니다.

여기서 말하는 부작용: Composable함수에서 UI를 표현하는행위 말고 다른행위도 하는것. 저는 의학적인 부작용이라는 말을 많이 들었어서 이해하는데 시간이 걸렸습니다🥲.

  • 공유된 객체의 프로퍼티에 쓰기
  • ViewModel의 관찰가능한 값을 업데이트 하기
  • Shared Preference를 업데이트 하기

재구성을 통해 UI를 업데이트하고자 할때, 다음 나열될 Composable함수의 특성을 숙지해두는게 좋습니다.

순서와 관계없이 실행될 수 있음

@Composable
fun ButtonRow() {
    MyFancyNavigation {
        StartScreen()
        MiddleScreen()
        EndScreen()
    }
}

위 코드에서 StartScreen(), MiddleScreen(), EndScreen()순서대로 실행될것이라는 보장이 없습니다. 따라서 StartScreen에서 상태를 변경해두면 MiddleScreen에서 변경된 값을 가져오는 등의 구현이 불가능 합니다. 이러한 함수들은 독립적이어야 합니다.

동시에 실행될 수 있음

Compose는 Composable함수를 동시에 실행시켜 재구성을 최적화 시킵니다. 이 말은 Composable함수가 백그라운드 스레드에서도 실행될 수 있다는 것을 의미합니다. 올바른 작동을 위해서 Composable함수에는 부작용이 없어야합니다. 대신 항상 UI스레드에서 실행되는 onClick과 같은 콜백에서 부작용을 트리거 합니다.

@Composable
@Deprecated("Example with bug")
fun ListWithBug(myList: List<String>) {
    var items = 0

    Row(horizontalArrangement = Arrangement.SpaceBetween) {
        Column {
            for (item in myList) {
                Text("Item: $item")
                items++ // 다른 Composable함수의 매개변수를 변경하는 코드를 피해야합니다.
            }
        }
        Text("Count: $items")
    }
}

이렇게 구현하면 items를 바꾸는 스레드와 Text()에서 items를 표현하는 스레드가 달라 items의 값이 잘못 될 수 있습니다. 위 코드는 다음과 같이 수정할 수 있습니다.

@Composable
fun ListComposable(myList: List<String>) {
    Row(horizontalArrangement = Arrangement.SpaceBetween) {
        Column {
            for (item in myList) {
                Text("Item: $item")
            }
        }
        Text("Count: ${myList.size}")
    }
}

가능한 많이 건너뜀

Compose는 업데이트가 필요한 부분만을 재구성하기 위해 최선을 다합니다.
모든 Composable함수는 자체적으로 재구성 될 수 있습니다. 따라서 모든 Composable함수에는 부작용이 없어야 합니다.

낙관적임

Compose는 매개변수가 다시 변경되기 전에 재구성을 완료할것이라고 예상합니다. 만약 재구성이 완료되기 전 매개변수가 다시 변경된다면, Compose는 실행중이던 재구성을 취소하고 다시 재구성을 실행할 수 있습니다. 이 과정에서 일관되지 않은 앱 상태가 발생할 수 있으므로 Composable함수가 여러번 호출되도 되는지, 부작용이 없는지를 확인해야 합니다.

매우 자주 실행될 수 있음

경우에 따라 Composable함수는 애니메이션의 모든 프레임에서 실행될 수 있습니다. 따라서 기기 저장소에서 읽기등의 비용이 많이 드는 작업을 실행하면 UI버벅임이 있을 수 있습니다.
이런 작업들은 외부 다른스레드로 이동하고 mutableStateOf또는 LiveData를 통해 Compose에 전달할 수 있습니다.

오늘은 Compose에 대해 알아보았습니다. 앞으로 Android공식 문서에 올라와있는 Compose자료들을 보면서 정리해볼 계획입니다. Compose를 제대로 알고 개발하는것과 제대로 모르고 개발하는것은 천지차이인 것 같습니다. 그럼 즐거운 개발 되세요~😄

참고자료 - Compose 이해

profile
항상 배울 것을 찾는 개발자입니다🔥.

0개의 댓글