Apple is Apple
article thumbnail
Published 2024. 3. 4. 22:17
[Android] Compose (RE) Android

1. Compose 

Jetpack Compose is Android’s recommended modern toolkit for building native UI. It simplifies and accelerates UI development on Android. Quickly bring your app to life with less code, powerful tools, and intuitive Kotlin APIs.

from Android Document

컴포즈는 안드로이드가 추천하는 네이티브 UI 작성을 위한 모던 툴킷이다.

컴포즈는 안드로이드의 UI 개발을 빠르고 간단하게 해줍니다. 어서 앱에 적용해서 적어진 코드, 강력한 도구들, 직관적인 API를 누려 보자.. (구글 문서 발췌)

 

컴포즈는 무엇이고, 왜 빠르게 성장하는가?

 

컴포즈와 전통 xml의 차이

xml의 단점

- 뷰의 계층과 속성 구조가 XML로 코드와 분리되서 관리 (불편함)
- XML 문법은 표현 내용에 비해 오버헤드가 있음  - ex) namespace
- 뷰에 대한 접근을 위해 findViewById 등과 같은 탐색이 필요함 (뷰 트리 탐색)
- 뷰의 상태 변경이 직접 노드에 API 접근을 통해 이루어짐
컴포즈의 장점 (구글 가이드)

 

- 적은 코드량
  - 계층 구조의 표현이 XML에 비해 간결
  - 데이터가 바인딩 된 구조기 때문에, 더 이상 뷰를 직접 찾아서 데이터를 변경해주지 않아도 됨
- 직관적인 코드
 -  선언형 UI 방식으로 순수하게 UI 포현에만 집중하기 때문에 직관적

  API 설계 자체도 XML에 비해 훨씬 직관적으로 변경

- 생산성 향상
 - XML을 오가면서 코딩하지 않아도 됨
- 강력한 도구 지원
 

중점 - 선언형 UI

--> UI를 어떻게 만드는 지에 대한 설정은 제거하고 오직 어떤 UI를 만들지에만 집중하는 방법론

2. Compose 작성법

2.1. @Composable 어노테이션을 붙여야함

@Composable 함수 -- > 컴포즈 뷰를 의미 -- 함수이지만 리턴을 하지 않음 (xml 처럼 계층 표현을 함수로 나타낸 것!)

 

Composable은 합성의 대상이되는 구성요소

단독으로 쓰일 수도 있고, Composable로 합성 된 뷰가 될 수도 있다 (중첩)

 

compose ui를 사용하려면 Composable 어노테이션을 선언해야함

@Composable은 클래스단위에는 붙일 수 없고, 함수 단위에 붙일 수 있음
@Composable을 붙어야 Text 같이 선언형 UI를 구현할 수 있음

 

Modifier

--> Modifier는 컴포넌트들을 꾸미는데 사용되는 수정자

--> Modifier는 기본적으로 먼저 호출된 설정이 우선 적용 됨

Modifier를 활용하면

1. 컴포넌트의 사이즈, 형태, 행위, 외면을 변경 가능

2. 컴포넌트에 내부 레이블 같은 추가적인 정보를 주입 가능

3. 클릭과 같은 유저 인풋 처리 가능

3. Compose가 화면을 그리는 방법 (라이프 사이클)

1. 화면을 그리는 시점에 해당 화면에 있는 컴포저블 함수를 파싱해서 메모리에 UI 트리 구조를 작성함. 이때 컴포저블 함수의 코드 블럭이 실행 됨 (코드를 메모리에 트리로 해석하는 과정을 Composition이라함)

(data가 바뀌어서 트리를 새로 그려야 하는 경우 Recomposition이라함  중요)

2. 만들어진 UI트리를 기반으로 실제 화면에 뷰를 그림, 이때 각 플랫폼에 맞는 렌더링 로직이 실행 (이 작업 high cost)

 

모든 트리를 다시 그리려면 비용이 너무 많이 드니 --> 필요한 것만 그리자 --> 메모리에서 UI트리의 변화를 추적하자 --> 가능한 많은 부분을 생략 (recomposition)

 

recomposition 주의 사항

컴포즈를 개발 할 때는 recomposition을 고려해서 작성해야함

recomposition으로 인해 컴포저블 함수는 이런 특성을 가짐

   - 순서대로 실행되지 않을 수 있음
   - 메인 쓰레드에서 실행되지 않을 수 있음 (여러 쓰레드에서 동시에 실행 될 수도 있음)
   - 빠르게 다시 실행될 수 있음

4. 상태 관리

Data가 바뀌어서 트리를 새로 그려야 하는 경우를 Recomposition 

하지만, 모든 Data를 다 감지할 수 있는 것은 아님

Compose는 기본적으로 함수의 인자만을 관찰할 수 있음

관찰 대상에 변수를 추가하기 위해서 remember 함수와 mutableState 타입을 사용

 

remember { mutableStateOf() } 를 이용하면 로컬 변수를 사용하는게 아니라 시스템 캐시에 값을 저장함리컴포지션이 되면 캐시에서 그 값을 가져와 값을 바꿔줌, 캐시가 없으면 초기값으로 세팅

 

상태 API

mutableStateOf - MutableState<T> 타입을 생성하는 api, 컴포즈가 상태를 기억하기 위해 사용, value 속성을 통해 값에 접근 할 수 있음

remember - 리컴포지션이 되어도 상태를 기억하도록 state를 캐싱하는 함

rememberSaveble - 화면 전환 등의 이유로 Activity가 재생성 되어도 상태를 기억하도록 state를 캐싱하는 함수

 

with delegate pattern (kotlin)

<kotlin />
// delegate pattern // value로 접근하지 않고 바로 count로 접근 가능 val count by remember { mutableStateOf(0) }

 

 

상태가 변경될 때 리컴포지션 과정

1. 이벤트가 발생하면 변수 상태의 변경이 일어남

2. 변수 값이 변경하면 컴포즈에게 상태가 바꼈다고 알림이 가고 리컴포지션이 일어남

3. 리컴포지션이 일어나면 변경된 값을 참조하는 부분을 찾아서 다시 그리기 시작

 

Stateful ---- Stateless

컴포저블 함수 내에 상태가 있다면 Stateful, 없으면 Stateless

상태가 있다면 함수의 재사용성이 떨어지고, 테스트가 어려움 (상태를 관리해야하는 코스트가 있기 때문에)

그래서 Stateless 하게 함수를 작성하는 것이 재사용성등 여러 가지 장점이 있음 (상태 고려를 적게 해도 됨)

 

--> 위에서 말한 것 처림 내부적으로 remember (상태)로 상태를 관리하면 Stateful한데, 상태 값과 이를 관리하는 로직을 주입 받으면 내부는 상태에서 free하게 되어 Stateless 상태가 됨

 

위 과정 Stateful 함수 -> Stateless 함수로 수정하는 과정을 State Hoisting이라고함 (State를 외부에서 주입 시킨다!)

5. 컴포즈 라이프 사이클

1.Enter the composition: 컴포저블 함수를 메모리에 적재하고 UI 트리를 그림
2.Recompose 0 or more times : 상태 데이터가 바뀌게 되면 Recomposition 수행
3.Leave the composition : 필요가 없어지면 UI 트리에서 삭제 
 
모든 각 컴포저블은 모두 각각의 생명주기를 갖고 있음 (Text, Button.... 각각)

6. 사이드 이펙트

사이드 이펙트는 컴포저블 함수 외부에서 일어나는 앱의 상태변화를 의미한다.

--> 뷰를 표현하는 것을 벗어나서, 인터넷 통신, snackbar 등 앱의 상태를 변경하는 활동을 컴포즈의 side effect라고함

 

기본적으로 컴포즈는 side effect가 없도록 작성해야한다.

--> 컴포저블 함수는 예측하지 못하게 recomposition이 일어날 수 있으므로 (위에서 기술한 3가지 이유...)

--> UI렌더링과 외부 상태 변경을 결합하면 안됨

 

기본적으로 사이드 이펙트는 피해야하지만, 개발하는데 있어 필요할 때도 있기 떄문에

컴포즈는 이를 처리하기 위해서 렌더링 라이프사이클에 맞춰서 앱의 상태를 변경할 수 있는 예측가능한 API를 제공함

 

LaunchedEffect

- 컴포저블 함수의 생명주기에 맞춰 한 번만 코드블럭을 실행, 취소 시켜줌

- 기본적으로 코루틴 스코프에서 실행됨

init composition - 실행 됨

recomposition - 무시 됨 (실행되지 않음) 

 - 만약 작업이 완료되지 않았다면, 이전 작업을 취소하고 다시 실행

decomposition - 무시 됨 (실행되지 않음)

- 만약 작업이 완료되지 않았다면, 이전 작업을 아예 취소

 

+ 키 값을 설정해주면 컴포저블의 생명주기 외에도 key값이 변경되었을 때도 재호출 됨

 

DisposableEffect

- Composition 시점에서 코드블럭이 실행 되었다가, Decomposition 시점에 onDispose 블럭이 실행됨

- 생성과 종료 시점을 나눠서 사용하고 싶을 때(등록, 삭), 주로 사용 (음악 재생, 정지..)

init composition - 실행됨

recomposition - 무시 됨 (실행되지 않음) 

decomposition - onDispose 실행

 

SideEffect

- 화면에 그려질 때마다 실행, 이전 작업을 취소하지 않고 한 번 실행된 작업을 끝까지 유지됨

- 화면 렌더링과 별개로 완료되어야하는 작업에서 주로 사용

init composition - 실행 됨

recomposition - 실행 됨

decomposition - 무시 됨

 

++ viewModel - compose 공부 중 추가 내용

ViewModel 인스턴스를 컴포저블 함수로 전달하지 마세요.

전달하면 구성 가능한 함수가 ViewModel 유형과 결합되어 재사용성이 떨어지고 테스트와 미리보기가 더 어려워지게 됨. 또한 ViewModel 인스턴스를 관리하는 명확한 단일 소스 저장소(SSOT)도 없습니다.

ViewModel을 전달하면 여러 컴포저블이 ViewModel 함수를 호출하고 상태를 수정할 수 있으므로 디버그하기가 더 어려워짐. 대신 UDF 권장사항을 따라 필요한 상태만 전달.

 

'Android' 카테고리의 다른 글

[Android] Compose - SwipeToDismiss  (1) 2024.06.03
[Android] Clean Architecture  (0) 2024.03.11
[Android] Dagger (basic)  (1) 2024.01.29
[Android] Compose (basic)  (1) 2024.01.15
[Android] 인앱 업데이트  (0) 2023.11.24
profile

Apple is Apple

@mjjjjjj