Compose란 무엇인가
Jetpack Compose는 Android용 네이티브 UI 도구 키트입니다.
Android에서 UI 개발을 간소화하고 가속화하도록 설계되었으며, 적은 코드, 직관적인 Kotlin API로 앱을 구현할 수 있습니다.
Compose 등장 배경
Android 개발자 문서에 따르면:
"UI 개발에 대한 기대치가 높아졌습니다. 오늘날 애니메이션과 모션을 포함한 세련된 사용자 인터페이스 없이는 앱을 구축하고 사용자의 요구를 충족시킬 수 없습니다. 이러한 요구사항은 현재 UI 툴킷이 만들어졌을 때는 존재하지 않았던 것들입니다. 세련된 UI를 빠르고 효율적으로 만드는 기술적 과제를 해결하기 위해 Jetpack Compose를 도입했습니다."
즉, Android 초기 버전( 2008년 - Android 첫 출시)부터 사용되어 온 기존 View 시스템은 당시에는 복잡한 애니메이션이나 동적인 UI 상호작용이 지금처럼 중요하지 않았습니다.
하지만 현재 사용자들은 부드러운 전환 효과와 반응성이 뛰어난 UI를 기대하게 되었고, 기존 시스템으로는 이런 요구를 효율적으로 충족하기 어려워졌습니다.
또한 공식 Compose 개요 문서는 Compose의 핵심 장점을 다음과 같이 설명합니다:
"적은 코드로 더 많은 작업을 수행하고 전체 클래스의 버그를 방지하므로 코드가 간단하고 유지 관리하기 쉽습니다. UI를 설명하기만 하면 Compose가 나머지를 처리합니다. 앱 상태가 변경되면 UI가 자동으로 업데이트됩니다."
이는 기존 View 시스템에서 개발자가 상태 변경 시마다 수동으로 UI를 업데이트해야 했던 복잡성을 해결한 것입니다.
선언형 UI vs 명령형 UI
명령형 UI (기존 View 시스템)
기존 Android View 시스템에서는 UI를 변경하기 위해 View의 상태를 수동으로 조작해야 했습니다. (파일자체는 xml도 선언형)
<!-- XML 레이아웃 -->
<LinearLayout ...>
<TextView
android:id="@+id/textView"
android:text="초기 상태" />
<Button
android:id="@+id/button"
android:text="클릭하세요" />
</LinearLayout>
// Activity에서 View 조작 (ViewBinding 사용)
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.button.setOnClickListener {
binding.textView.text = "클릭됨" // View를 직접 조작
binding.textView.setTextColor(Color.RED)
binding.button.isEnabled = false
}
}
}
선언형 UI (Compose)
Compose에서는 현재 상태에 따라 UI가 어떻게 보여야 하는지만 기술합니다.
@Composable
fun MyScreen() {
var isClicked by remember { mutableStateOf(false) }
Column {
Text(
text = if (isClicked) "클릭됨" else "초기 상태",
color = if (isClicked) Color.Red else Color.Black
)
Button(
onClick = { isClicked = true },
enabled = !isClicked
) {
Text("클릭하세요")
}
}
}
Compose 구조와 동작 흐름 분석
다음은 Empty Activity 템플릿에서 생성되는 기본 코드입니다:
class MainActivity : ComponentActivity() { // 1. ComponentActivity 상속
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge() // 2. Android 시스템 UI 설정
setContent { // 3. Compose 시작점
StudyMateTheme { // 4. 테마 적용
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> // 5. 화면 구조
Greeting( // 6. 사용자 정의 Composable
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
코드 라인별 분석
1. ComponentActivity 상속
- 기존 AppCompatActivity 대신 Compose 전용 Activity
- Compose UI를 호스팅하는 데 최적화된 Activity
2. enableEdgeToEdge()
- Android 15(API 35)에 도입된 시스템 UI API
- 상태바, 네비게이션바와 관련된 Android 플랫폼 설정
3. setContent { }
- Compose UI의 시작점
- 기존 setContentView(R.layout.activity_main) 대신 사용
- 중괄호 안의 코드가 Compose UI 트리를 구성
4. StudyMateTheme { }
- Material Design 테마를 적용하는 Composable
- 색상, 타이포그래피, 모양 등의 디자인 시스템 정의
5. Scaffold
- Material Design의 기본 화면 구조를 제공하는 Composable
- 상단바, 하단바, Floating Action Button 등을 배치할 수 있는 틀
- innerPadding으로 시스템 UI와 겹치지 않도록 여백 제공
6. Greeting Composable
- 사용자 정의 UI 컴포넌트
- modifier를 통해 레이아웃과 스타일 적용
Compose UI 트리 구조
ComponentActivity
└── setContent
└── StudyMateTheme
└── Scaffold
└── Greeting
└── Text
@Composable 어노테이션과 Activity의 관계
@Composable 어노테이션의 역할
@Composable 어노테이션은 함수가 Compose UI 컴포넌트임을 컴파일러에게 알려주는 표시입니다.
이 어노테이션이 붙은 함수들은 특별한 컴파일 과정을 거쳐 UI 트리를 구성하는 코드로 변환됩니다.
ComponentActivity와 @Composable 함수의 관계
class MainActivity : ComponentActivity() { // Android 시스템 컴포넌트
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { // Compose 진입점
StudyMateTheme { // @Composable 함수
Scaffold { innerPadding -> // @Composable 함수
Greeting("Android") // @Composable 함수
}
}
}
}
}
관계도:
- ComponentActivity: Android 시스템과 앱 생명주기를 관리
- setContent { }: Activity와 Compose UI 간의 연결점
- @Composable 함수들: UI 구성 요소들이 트리 형태로 중첩
즉 Activity는 Compose UI의 "컨테이너" 역할을 한다고 볼 수 있습니다. 실제 UI는 @Composable 함수들이 담당합니다.
Activity 생명주기와 Compose 생명주기는 독립적으로 동작하지만, Activity가 종료되면 포함된 모든 Composable도 함께 제거됩니다.
Compose 생명주기
Compose에서 Composable의 생명주기는 다음과 같이 정의됩니다:
Entering the Composition > Recomposition > Leaving the Composition

@Composable
fun CounterExample() {
var count by remember { mutableStateOf(0) }
// 1단계: Entering the Composition - 처음 호출될 때
println("Composable 함수 실행 - 현재 count: $count")
Column {
Text("Count: $count")
Button(
onClick = {
count++ // 2단계: Recomposition 트리거
// 상태가 변경되면 이 Composable이 다시 실행됨
}
) {
Text("증가")
}
}
// 3단계: Leaving the Composition
// - 이 Composable이 UI 트리에서 제거될 때 발생
// - 별도 코드 없이도 자동으로 처리됨
// - 정리 작업이 필요한 경우에만 DisposableEffect 사용
DisposableEffect(Unit) {
println("리소스 할당")
onDispose {
println("리소스 정리") // 정리 작업 예시
}
}
}
생명주기 단계별 설명:
- Entering the Composition: Composable이 UI 트리에 처음 추가될 때
- Recomposition: 상태 변경으로 인해 Composable이 다시 실행될 때 (0번 이상)
- Leaving the Composition: Composable이 UI 트리에서 제거될 때
Activity 생명주기와의 차이점:
- Activity: 앱 수준의 생명주기 (onCreate, onStart, onResume 등)
- Compose: UI 요소 수준의 생명주기, 상태 변경에 따라 개별적으로 관리
@Composable 함수 컨벤션
네이밍 규칙
// ✅ 올바른 명명 - Unit을 반환하는 @Composable
@Composable
fun UserProfile() { } // PascalCase, 명사형
@Composable
fun LoginButton() { } // UI 요소를 나타내는 명사
// ✅ 값을 반환하는 @Composable
@Composable
fun rememberUserState(): UserState // camelCase + remember 접두사
// ❌ 잘못된 명명
@Composable
fun showUserProfile() { } // 동사형 (X)
@Composable
fun userProfile() { } // camelCase (X) - Unit 반환 시
매개변수 규칙
@Composable
fun UserCard(
user: User, // 1. 필수 매개변수
onClick: () -> Unit, // 2. 이벤트 콜백
modifier: Modifier = Modifier // 3. modifier는 첫 번째 선택적 매개변수
) {
// 구현
}
@Preview란 무엇인가
공식문서에 따르면, @Preview는 "Android Studio 내에서 Composable 함수를 미리 보기 위한 도구"입니다.
앱을 실행하지 않고도 UI가 어떻게 표시될지 확인할 수 있어 개발 효율성을 크게 향상시킵니다.
@Preview 속성표
| 속성 | 타입 | 기본값 | 설명 | 사용 예시 |
| name | String | "" (함수명) | 프리뷰 제목 설정 | name = "Light Mode" |
| group | String | "" | 프리뷰 그룹화로 필터링 가능 | group = "Themes" |
| showBackground | Boolean | false | 배경 표시 여부 | showBackground = true |
| backgroundColor | Long | 0 | 배경색 (32비트 ARGB) | backgroundColor = 0xFFFFFFFF |
| showSystemUi | Boolean | false | 상태바/네비게이션바 표시 | showSystemUi = true |
| device | String | Devices.DEFAULT | 디바이스 설정 | device = "id:pixel_4" |
| uiMode | Int | 0 | UI 모드 (다크/라이트) | uiMode = UI_MODE_NIGHT_YES |
| fontScale | Float | 1.0f | 폰트 크기 배율 | fontScale = 1.5f |
| widthDp | Int | -1 | 프리뷰 너비 (dp) | widthDp = 320 |
| heightDp | Int | -1 | 프리뷰 높이 (dp) | heightDp = 640 |
| locale | String | "" | 언어/지역 설정 | locale = "ko-rKR" |
| apiLevel | Int | -1 | Android API 레벨 | apiLevel = 33 |
@Preview 사용법
@Preview(showBackground = true) // 배경 표시
@Preview(
uiMode = Configuration.UI_MODE_NIGHT_YES, // 다크 모드
name = "Dark Mode" // 프리뷰 이름
)
@Composable
fun GreetingPreview() {
StudyMateTheme { // 테마 적용 권장
Greeting("Android")
}
}
@Preview 제한사항:
- 매개변수가 없는 @Composable 함수에만 사용 가능
- 매개변수가 필요한 경우 @PreviewParameter 사용 필요
- 실제 기기/에뮬레이터의 context와 동일하지 않음
View 시스템과 Compose의 차이점
코드 구성
| 구분 | View 시스템 | Compose | 설명 |
| UI 정의 | XML + Kotlin/Java | Kotlin만 사용 | UI 레이아웃 정의 방식 |
| View 참조 | ViewBinding/findViewById 필요 | ID 참조 불필요 | UI 요소 접근 방법 |
| 파일 구성 | 별도 레이아웃 파일 | 함수 내에서 UI 정의 | 코드와 UI의 분리/통합 정도 |
상태 관리 방식 & UI 업데이트 방식
| 구분 | View 시스템 | Compose |
| 상태와 UI 동기화 방식 | 상태가 변경되면 관련된 View들을 찾아서 수동으로 업데이트 | 상태가 변경되면 해당 부분이 자동으로 재구성됨 |
| UI 변경의 제어 주체 | 개발자가 직접 View 속성 변경 | 상태에 따라 UI가 자동으로 결정됨 |
참고 자료 - Android 공식 문서
'CS 기초부터 한 걸음씩' 카테고리의 다른 글
| [WSL2] 윈도우-리눅스 파일 시스템 상호운용성 정리 (0) | 2026.02.26 |
|---|---|
| [Android] Fragment 개념 정리 (4) | 2025.08.21 |
| [Android] XML 레이아웃과 View 기본 개념 (0) | 2025.08.20 |
| [Android] 안드로이드 앱 구조 정리 (1) | 2025.08.19 |
| [Android] Android Studio Logcat과 로그 레벨 (3) | 2025.08.18 |