BigJeon Android 개발 블로그

Compose의 화면 갱신 원리에 관하여... 본문

AOS - Compose

Compose의 화면 갱신 원리에 관하여...

Big-Jeon 2024. 8. 15. 13:07
반응형

최근 Compose에 대하여 집중적으로 살펴보던 와중 Compose의 화면 갱신 방식과 이에 발생 가능한 문제점 등을 알아보고자한다.

1. Compose의 작동 원리.

우선 안드로이드 Compose Docs를 살펴보도록 하자.

Compose의 경우 특정 View를 선언하고 해당 OnClick(), 특정 값의 변경 등의 이벤트 발생 시 Setter()를 통해 화면을 재구성 하게 된다.

이때 Compose의 장점이 나타난다.

만약 Compose를 활용한 View를 재구성 할 때 해당 View의 전체 내용을 재구성 하는 방식으로 화면을 재구성 했다면 느린 렌더링과 컴퓨팅 비용이 크게 증가했을것이다.(이러한 문제를 실제 Compose개발 한 개발진들도 겪었던 문제이며, 이를 해결하기 위해 노력을 많이 했을거 같다.)

*물론 특정 변경 사항을 감지하는 로직 때문에도 비용이 증가 할 수 도 있지 않은가?라는 생각이 들기는해서 찾아볼 예정이다.*

 

이를 방지하기 위해 도입된 방식이 바로

메게변수가 변경되지 않은 함수 또는 람다는 건너 뛰기

이다.

 

쉽게 말해 특정 버튼의Text의 값을 바꾼다고 하였을때 컴포즈의 경우 다음과 같이 구성이된다.

@Composable
fun ClickCounter(clicks: Int, onClick: () -> Unit) {
    Button(onClick = onClick) {
        Text("I've been clicked $clicks times")
    }
}

해당 코드를 살펴보면 Button에 onClick()이벤트 발생 시 Click된 횟수를 나타내게 된다.

만약 사용자가 해당 버튼을 클릭 하였다고 했다면, Compose는 다음과 같은 방식으로 화면을 갱신 시키게 된다.

 

1. 메게변수 변경 내용 확인

2. 메게변수가 변경 되지 않은 함수 또는 람다 건너뛰기

3. 변경이 확인된 함수 또는 람다 함수 재 실행

4. 재구성 완료

 

이러한 특징과 더불어 Compose는 함수를 동시에 수행 시킨다.

 

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

 

위의 코드를 보게되면 우리는 당연히 StartScreen()함수가 실행 되고 이후에 MiddleScreen(), EndScreen()순서로 실행 될것이라 생각한다.

하지만 Android Docs문서 확인 결과 다음과 같은 문구가 적혀있다.

StartScreen, MiddleScreen 및 EndScreen 호출은 순서와 관계없이 발생할 수 있습니다. 즉, 예를 들어 
StartScreen()이 일부 전역 변수(부작용)를 설정하고 
MiddleScreen()이 이러한 변경사항을 활용하도록 할 수 없음을 의미합니다. 대신 이러한 각 함수는 독립적이어야 합니다.

 

즉 함수의 실행에 대하여 순서가 없다는 뜻이다.

우선 이부분 까지만 알아보고 부작용에 대하여 알아본뒤 다시 살펴 보도록 하자.

 

2. 부작용이란?

사실 부작용이란 내용은 Compose뿐만 아니더라도 다양하게 발생한다.

필자가 개발하면서 겪었던 경험으로는 다음과 같은 경우가 있다.

 

다수의 View에서 공통적으로 참조되는 변수의 내용이 변경 되었을때, 변수에 Data가 할당되는 시점과 해당 Data를 참조하는 View들의 상태를 변경하는 로직들의 시점 차이로 인한 UI 표시 데이터 불일치.

 

실제로 개발을 진행하면서 프로젝트의 규모가 커지고 플로우가 복잡해 질수록 해당 이슈가 빈번히 발생 하였고,

해당 문제를 해결 하기 위해 View 갱신 로직을 수정 한다 던지, 변수를 직접 참조 하는게 아닌 파라미터로 입력을 받아 전달 한다던지 등의 수정을 진행하였고, 정말 큰 경우 아예 프로젝트의 디자인 패턴 자체를 변경 했던 경험도 있다.

 

위와 같은 문제와 위에서 말했던 Compose의 작동 원리를 보면 예상 가는 문제가 있을 것이다.

 

만약 하나의 변수를 여러개의 함수가 할당 받고 있다면?

 

만약 그러한 경우 예상되는 문제는 다음과 같을 것이다.

 

A라는 변수를 할당 받는 methodA(), methodB(), methodC()라는 함수가 있다고 했을때 A라는 변수의 값이 바뀌었다면?

methodA()가 A라는 변수를 참조하여 화면을 갱신할때와  methodB(), methodC()가 A라는 변수를 할당 받았을때의 A라는 변수의 할당 시점이 다를 수 있기에, 제각기 다른 값을 표현하고 있을 위험이 있다. 왜냐 Compose는 함수를 순서대로 작동 시키지 않고, 더 나아가 만약 A라는 변수가 변경 되지않았다고 판단 하게 되는 순간 해당 메서드는 그냥 건너뛰기를 해버릴 것이기 때문이다.

 

이러한 경우는 Compose이기 때문에?라기 보단 Compose에서 발생 가능성이 더욱 빈번 할 수 있기에 더욱 코드 작성시 조심 해야한다는것 같다. 부작요의 발생은 곧 사용자에게 있어 UI의 불일치를 발생시키고, 만약 불일치를 알아 차리지 못하고 개발을 지속적으로 진행 하게 된다면 추후 해당 불일치의 발생 원일을 찾기에 더 많은 비용이 발생 할 것으로 보인다.

 

 

3. 부작용 방지 방법.

우선 부작용이 발생 하는 경우의 수를 간단하게 나열하자면

  • 공유 객체의 속성에 쓰기
  • ViewModel에서 식별 가능한 요소 업데이트
  • 공유 환경설정 업데이트

등이 있다. 물론 이외에도 경우의 수는 존재 하지만 사실 방지 방법이 모두 비슷해서 이정도만 적어 두겠다.

해당 내욜들을 보면 사실 CleanCode라는 책에서 나오는 내용과 겹치는 부분이 많다. 전역 변수를 함부로 바꾸지 말아라...

하나의 메서드는 하나의 기능만 하도록 설계해라, 각가의 함수는 독립적으로 실행 되어야 한다 등등의 내용이 있었던 것으로 기억한다.

(현재 많이 까먹어서 다시 읽어볼 생각이며, 굉장히 추천 하는 책이다.)

방지 방법이라고 해서 딱 정해져있는건 없는거 같고 위에 말했던 클린코드(CleanCode)라는 책을 읽어보고 그 책에서 권장하는 코드 스타일을 따라 하다보면 자연스레 부작용 발생 빈도수가 낮아 질것이라 생각한다.

 

해당 책을 다시 읽으며 내용을 정리하는 포스팅을 여러번에 나눠서 포스팅 할 예정이니, 해당 포스팅을 참고 해 주길 바란다

 

반응형