렌더링 성능에 대해 글을 쓰기 전에 간단히 렌더링이 무엇인지 정리해본다.
렌더링(Rendering)은 간단히 말하면, 데이터를 화면에 보이는 형태로 그려주는 과정을 말한다.
조금 풀어서 설명하면:
- 안드로이드 앱에서는 코드나 데이터(예: 텍스트, 이미지, 버튼 등)가 실제로 눈에 보이는 화면(UI)으로 변환되는 과정을 "렌더링"이라고 부른다.
- 예를 들면, 버튼을 만들었다면 → 이걸 "그리는 작업" → 유저가 터치할 수 있게 화면에 보여주기까지 이 전체 과정이 렌더링이다.
예를 들면:
- 개발자가: TextView에 "Hello World"를 설정함 → (데이터)
- 앱이: 이 텍스트를 화면에 맞는 글꼴, 색상, 위치로 "그려서" 보여줌 → (렌더링)
렌더링 왜 중요할까?
- 렌더링이 느리면 화면이 버벅이거나, 스크롤할 때 끊김이 발생한다.
- 렌더링이 최적화되어야 부드럽게 터치, 스크롤, 애니메이션 등이 작동한다
요약
렌더링 = 데이터를 화면에 보이게 만드는 과정
빠르고 부드러운 렌더링 = 좋은 사용자 경험 (UX)
앱에서 렌더링이 느리면? 사용자는 그냥 "앱이 별로구나"라고 느껴진다.
UI는 곧 UX, 이번엔 화면 렌더링 성능을 높이는 핵심 팁이라 앱 성능 최적화 영향을 미친다.
1. Recomposition 최소화 (Jetpack Compose)
- remember, derivedStateOf, key() 등 Compose의 recomposition 제어 도구를 적극 사용한다.
val derived = remember(myValue) {
derivedStateOf { myValue + 1 }
}
1) Recomposition이란?
- Compose는 화면(UI)을 "상태(State)" 에 따라 그린다.
- 상태가 바뀌면, 바뀐 부분만 다시 그리는 작업을 하는데, 이걸 Recomposition(리컴포지션) 이라고 부른다.
쉽게 말하면 👉 "아, 상태가 변했네? 다시 그려야겠다!" 하는 순간이 바로 리컴포지션이다.
2) 그런데 왜 "최소화"해야 할까?
- 리컴포지션은 필요할 때만 일어나야 효율적이에요.
- 불필요하게 자주 일어나면 ➔ CPU 사용량 증가 ➔ 배터리 소모 ➔ 앱이 버벅거림.
- 그래서 "필요할 때만 최소한으로" 리컴포지션이 발생하도록 신경 써야 합니다.
3) Recomposition 최소화를 위한 기본 방법
remember 사용 | 변하지 않는 값은 기억해서, 불필요한 리컴포지션을 막는다. |
derivedStateOf 사용 | 다른 상태(state)를 기반으로 파생된 값이 필요할 때 효율적으로 관리한다 |
Composable 함수 쪼개기 | 작은 단위로 쪼개서, 필요한 부분만 다시 그리게 한다. |
key를 잘 활용 | 리스트나 반복문에서 항목이 고유하게 식별되게 한다. |
Stable 데이터 사용 | 데이터 클래스에 불필요한 변화를 주지 않도록 설계한다. |
LaunchedEffect, rememberUpdatedState | 비싼 작업(예: API 호출)이 매번 재실행되지 않게 막는다. |
비유하면
상태가 바뀔 때마다 집 전체를 리모델링하는 건 비효율적?
필요한 방만, 필요한 가구만 고쳐야 빠르고 효율적인 것과 같다.
4) 아주 간단한 예
❌ 리컴포지션이 너무 많이 일어나는 코드
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Text("Count: $count")
Button(onClick = { count++ }) {
Text("Add")
}
}
여기선 버튼, 텍스트 모두가 매번 다시 그려진다.
✅ 리컴포지션 범위를 줄인 코드
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Column {
CountText(count) // 별도로 분리
Button(onClick = { count++ }) {
Text("Add")
}
}
}
@Composable
fun CountText(count: Int) {
Text("Count: $count")
}
이제 버튼은 리컴포지션 안 되고, 텍스트만 다시 그린다. (효율적!)
5) Recomposition을 효율적으로하면?
- 상태가 변할 때 필요한 부분만 다시 그리는 게 목표.
- remember, derivedStateOf, Composable 쪼개기를 적극 활용.
- "변하지 않는 부분"은 리컴포지션 안 일어나게 잘 관리!
2. Layout Overdraw 줄이기 (View 방식)
- 화면에 같은 픽셀을 여러 번 그리는 일이 없도록 Layout Inspector로 중복된 뷰 제거
- 배경 중복, 투명 배경 조심!
3. 무거운 작업은 UI 쓰레드에서 분리
- ViewModelScope.launch(Dispatchers.IO)로 데이터 로딩 등은 백그라운드에서 처리
- UI 쓰레드에서 block 되는 작업은 반드시 피하기
4. ConstraintLayout 활용
- 중첩된 LinearLayout보다 성능에 훨씬 유리
- 불필요한 Nested Layout은 피하기
5. RecyclerView 성능 개선
- setHasFixedSize(true) 설정
- DiffUtil을 사용한 효율적인 데이터 변경
- ViewHolder 재사용 제대로 하기
6. 애니메이션 과도하게 사용 금지
- 너무 무거운 애니메이션은 렌더링 병목의 원인이 됩니다.
- MotionLayout은 적절히 사용하되, 성능에 유의할 것
7. 정리
컴포즈의 Recomposition에 대한 설명이 중요하다 생각하여 정리가 길어졌지만, 다른 항목들은 간단하게 정리했다.
렌더링 성능을 잘 조절하면, 앱이 '가볍고 자연스럽다'는 인상을 줄 수 있고, 하드웨어 성능에만 의존하지 않고도 깔끔하고 매끄러운 UX를 만들 수 있다.
성능 좋은 UI가 최고의 마케팅 수단이 될 수 있다고 생각하기 때문에, 렌더링 최적화는 그만큼 중요한 부분이라고 생각한다.
'Android' 카테고리의 다른 글
[안드로이드] Play Store 심사 탈락 사례 모음 & 대응 방법 (0) | 2025.05.06 |
---|---|
[안드로이드] NetworkOnMainThreadException 발생 원인과 해결 방법 (0) | 2025.05.01 |
[안드로이드] 앱 성능 최적화 : 네트워크 최적화 (0) | 2025.04.27 |
[안드로이드] 앱 성능 최적화 : 메모리 최적화 (1) | 2025.04.26 |
[안드로이드]커스텀 EditText 만들기 (1) | 2025.04.22 |