Jetpack Compose는 2021년 7월 안정 버전 1.0이 출시된 Google의 Android 개발용 최신 선언형 UI 프레임워크입니다. Compose는 명령형 XML 레이아웃과 View 시스템을 @Composable 어노테이션이 있는 Kotlin 함수로 대체합니다. Google I/O (2025)에 따르면, Compose는 신규 Android 프로젝트의 55%에서 사용되며, UI 코드의 평균 크기는 XML에 비해 30~50% 감소합니다. 프레임워크는 State 변경 시 화면에서 변경된 부분만 자동으로 다시 그리며, notifyDataSetChanged 또는 findViewById의 수동 호출이 필요 없습니다.
핵심 요점
Jetpack Compose는 반응형 프로그래밍 원칙에 기반하여 구축된 Google의 UI 프레임워크입니다. XML 레이아웃, FragmentManager 및 ViewBinding을 사용하는 대신, 개발자는 @Composable 어노테이션이 있는 Kotlin 함수를 통해 인터페이스를 설명합니다. Compose는 LayoutInflater를 사용하지 않습니다. UI는 바이트코드로 컴파일되는 순수 Kotlin 함수입니다. 프레임워크는 여러 라이브러리로 구성됩니다: Compose UI(기본 컴포넌트 Text, Button, Column, Row), Compose Foundation(Material Design, Gestures, Focus), Compose Material3(Dynamic Colors를 지원하는 Material You) 및 Compose Runtime(State, Side Effects, Coroutines). Compose는 Android API 21+(Android 5.0)에서 작동하며 ComposeView(AndroidView 어댑터)를 통해 기존 View/XML 코드와 완벽하게 호환됩니다.
View System(2008)은 수동 관리로 View 객체의 계층 구조를 사용합니다. 개발자는 XML 레이아웃을 만들고, findViewById로 View를 찾고, RecyclerView용 Adapter를 구현하고, 데이터 변경 시 notifyDataSetChanged를 호출합니다. Compose는 UI를 함수적으로 설명합니다. 상태가 변경되면 프레임워크는 변경된 Composable 함수만 다시 컴포즈하고, diff를 계산하여 Canvas에 최소한의 변경 사항을 적용합니다. Compose의 성능은 View System과 비슷하며, 복잡한 목록(LazyColumn) 시나리오에서는 key 매개변수를 통해 불필요한 요소를 건너뛰어 RecyclerView를 종종 능가합니다. IT Sectr에서는 Compose가 minSdk 24+의 Android 애플리케이션 새 모듈에 사용됩니다.
@Composable — 일반 Kotlin 함수를 UI 구성 요소로 변환하는 어노테이션입니다. Composable 함수는 다른 Composable 함수를 호출하여 컴포넌트 트리를 형성할 수 있습니다. View와 달리 Composable 함수는 기본적으로 상태가 없습니다. stateless이며 입력 매개변수나 State가 변경될 때마다 다시 그려집니다. Compose Compiler는 @Composable 함수를 중단 및 재개 가능한 코드(positional memoization)로 변환합니다. 이를 통해 Compose는 트리를 완전히 재구축하지 않고 변경된 부분만 다시 실행할 수 있습니다.
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun UserProfile(name: String, avatarUrl: String) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
AsyncImage(
model = avatarUrl,
contentDescription = "User avatar",
modifier = Modifier.size(48.dp)
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = name,
style = MaterialTheme.typography.titleMedium
)
}
}UserProfile 함수는 name과 avatarUrl을 매개변수로 받아 아바타와 이름이 있는 행을 구성합니다. Modifier.fillMaxWidth()는 컴포넌트를 전체 너비로 늘리고, .padding(16.dp)은 패딩을 추가합니다. MaterialTheme.typography.titleMedium은 시스템 Material 3 스타일을 사용합니다. name 또는 avatarUrl 매개변수가 변경되면 Compose가 UserProfile을 다시 컴포즈하여 새 값으로 컴포넌트를 재생성합니다.
State는 Compose에서 변경 시 종속된 @Composable 함수의 재구성(다시 시작)을 트리거하는 모든 값입니다. 기본 방법은 mutableStateOf()로, Compose의 스냅샷 시스템을 지원하는 MutableState<T>를 생성합니다. 스냅샷은 변경 추적 메커니즘입니다. State에 쓰면 Compose가 현재 스냅샷을 dirty로 표시하고, 재구성 스케줄러는 해당 State를 읽는 Composable 함수만 다시 시작합니다. 관찰 가능한 컬렉션에는 mutableStateListOf()와 mutableStateMapOf()를 사용합니다. Compose는 Kotlin Flow용 collectAsState()와 LiveData용 observeAsState()를 통해 Lifecycle-Aware 컴포넌트의 State를 지원합니다.
@Composable
fun LikeButton() {
var liked by remember { mutableStateOf(false) }
var count by remember { mutableStateOf(42) }
Button(
onClick = {
liked = !liked
if (liked) count++ else count--
},
colors = ButtonDefaults.buttonColors(
containerColor = if (liked) MaterialTheme.colorScheme.primary
else MaterialTheme.colorScheme.surfaceVariant
)
) {
Text("♥ $count")
}
}이 코드는 mutableStateOf()를 통해 저장된 liked(Boolean)와 count(Int) 상태를 가진 "Like" 버튼을 만듭니다. 누르면 liked가 반전되고 카운터가 증가/감소합니다. Compose가 LikeButton을 자동으로 다시 컴포즈합니다. by 위임자를 사용하면 liked.value = !liked.value 대신 liked = !liked처럼 State를 일반 변수처럼 사용할 수 있습니다. 버튼 색상은 primary(좋아요)와 surfaceVariant(좋아요 아님) 사이에서 반응형으로 변경됩니다.
remember — 재구성 간에 값을 캐싱하는 Compose 함수입니다. remember가 없으면 Composable 함수가 다시 시작될 때마다 새로운 상태(mutableStateOf)가 생성되어 애플리케이션 로직이 깨집니다. 화면 회전이나 부모 State 변경 시 카운터가 리셋됩니다. remember는 계산 람다를 받아 첫 번째 컴포지션에서만 값을 계산하고 이후 컴포지션에서는 캐시된 결과를 반환합니다. 캐시를 재설정하려면 remember(key)를 사용합니다. 키는 값을 재계산할 시기를 결정합니다. remember(key1, key2)는 키가 변경되면 재계산합니다. 장기 지속 상태(화면 회전에도 유지)에는 rememberSaveable을 사용합니다. 이는 SavedStateHandle을 통해 Bundle에 저장하는 remember의 유사 기능입니다.
@Composable
fun TimerScreen() {
var seconds by rememberSaveable { mutableStateOf(0) }
LaunchedEffect(Unit) {
while (true) {
delay(1000)
seconds++
}
}
Column(
modifier = Modifier.fillMaxSize().padding(32.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Elapsed: ${seconds}s",
style = MaterialTheme.typography.displayMedium
)
}
}rememberSaveable은 화면 회전(구성 변경) 시 seconds를 유지합니다. LaunchedEffect는 매초 seconds를 증가시키는 코루틴을 시작합니다. TimerScreen의 재구성(매초) 시 rememberSaveable 덕분에 seconds가 리셋되지 않습니다. 구성 변경을 견딜 필요가 없는 상태(예: 애니메이션 플래그)에는 일반 remember를 사용합니다.
Modifier — Composable 컴포넌트의 모양, 동작 및 레이아웃을 구성하기 위한 Builder 패턴을 구현한 불변 컨테이너 객체입니다. 각 Modifier 메서드 호출(.padding(), .width(), .clickable(), .background())은 요소가 추가된 새 Modifier를 반환합니다. Modifier 순서가 중요합니다: .padding(16.dp).clickable { } — 패딩이 클릭 핸들러 전에 적용되며, clickable은 패딩을 포함한 전체 영역을 추적합니다. .clickable { }.padding(16.dp) — 패딩 전의 전체 영역이 클릭 가능하며, 패딩이 콘텐츠를 안쪽으로 이동시킵니다. Modifier는 then(otherModifier)를 통한 사용자 정의 구현과 상위 설정에 액세스하기 위한 compositionLocal을 지원합니다.
@Composable
fun CardExample() {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.clickable { println("Card clicked") },
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant
)
) {
Column(modifier = Modifier.padding(16.dp)) {
Text("Jetpack Compose", style = MaterialTheme.typography.titleLarge)
Spacer(modifier = Modifier.height(8.dp))
Text(
"State 및 Modifier를 사용한 Android용 선언형 UI",
style = MaterialTheme.typography.bodyMedium
)
}
}
}CardExample은 Modifier 체인(fillMaxWidth + padding + clickable)을 보여줍니다. Card는 자체 elevation 및 colors 설정이 있는 Material3 컴포넌트입니다. 중첩된 Column은 내부 패딩이 16dp입니다. Compose Material3는 Android 12+에서 Dynamic Color(Material You)에 자동으로 색상을 적용합니다. Card는 Compose에 내장된 30개 이상의 Material3 컴포넌트 중 하나입니다.
State hoisting — Composable 함수에서 호출자로 상태를 끌어올려 함수를 stateless로 만드는 패턴입니다. Stateless 컴포넌트는 State를 직접 소유하지 않고 매개변수를 통해 데이터와 콜백을 받습니다. 이는 재사용성과 테스트 가능성을 향상시킵니다. 컴포넌트를 다른 States로 호출하고 preview에서 테스트할 수 있습니다. Google이 권장하는 아키텍처는 ViewModel + StateFlow(또는 MutableStateFlow) + Compose의 collectAsState()입니다. ViewModel이 상태를 화면 수준으로 끌어올리고 Compose가 렌더링합니다. 부작용(LaunchedEffect, DisposableEffect)은 일회성 이벤트를 처리합니다.
| 컴포넌트 | 책임 | 기술 |
|---|---|---|
| ViewModel | State 저장, 비즈니스 로직 | StateFlow, MutableStateFlow |
| Screen Composable | 화면 구성, State hoisting | collectAsState(), Scaffold, LazyColumn |
| Stateless Component | 상태 없는 UI, 순수 렌더링 | @Composable 매개변수 + 람다 |
| Side Effect | 일회성 작업(스낵바, 네비게이션) | LaunchedEffect, SnackbarHostState |
자주 묻는 질문
XML 레이아웃은 명령형입니다. LayoutInflater가 XML을 로드하고, findViewById가 View를 찾고, 개발자가 수동으로 텍스트/목록을 업데이트합니다. Compose는 선언형입니다. UI는 Kotlin @Composable 함수로 설명되며, 프레임워크가 State 변경 시 변경된 부분만 자동으로 다시 그립니다. Compose는 UI 코드 양을 30~50% 줄이고 XML을 사용하지 않습니다.
네, Google은 Navigation Compose를 제공합니다. 화면 간 선언형 네비게이션을 위한 라이브러리입니다. NavController가 경로 스택을 관리하고, composable()이 화면을 등록하며, navArgument가 매개변수를 전달합니다. 대안: Voyager(커뮤니티) 및 Decompose(Badoo). Navigation Compose는 딥 링크와 타입 안전 인수를 지원하는 공식 표준입니다.
Modifier는 Composable 컴포넌트에 적용되는 장식 패턴(padding, size, clickable, background, border, clip)의 불변 컨테이너입니다. 각 호출은 요소가 추가된 새 Modifier를 반환합니다. 순서가 중요합니다: .padding().clickable()은 클릭 전에 패딩을 적용하고, .clickable().padding()은 그 반대입니다. Modifier는 Compose 컴포넌트를 사용자 정의하는 주요 방법입니다.
Jetpack Compose는 Compose Compiler와 Compose BOM을 통해 Android API 21+(Android 5.0 Lollipop)를 지원합니다. Material 3(Dynamic Colors)는 Android 12+에서 사용 가능하며, 이전 버전에서는 폴백 팔레트가 사용됩니다. 새 프로젝트의 경우 Google은 minSdk 24+(Android 7.0)를 권장하며, 이는 Compose에서 완전히 지원됩니다.
Google은 Compose UI Test를 제공합니다. JVM(데스크톱) 및 에뮬레이터에서 테스트하기 위한 라이브러리입니다. 주요 함수: composeTestRule.setContent { }로 컴포넌트 렌더링, onNodeWithText()로 요소 찾기, performClick()/performTextInput()으로 작업 수행. Espresso 테스트는 ComposeTestRule을 통해 Compose와 호환되지만, Compose UI Test가 선호되는 더 빠른 접근 방식입니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.