
Jetpack Compose와 XML 차이를 물으면 저는 먼저 선택 문제가 아니라고 말합니다. 실제 안드로이드 프로젝트는 새 화면은 Compose로 가고, 오래된 화면은 XML이 남아 있는 식으로 섞여 있는 경우가 많기 때문입니다.
결론은 간단합니다. 새로 배우는 축은 Compose로 잡고, XML은 읽을 수 있을 정도로 함께 가져가는 편이 좋습니다. Compose는 상태 중심 사고를 익히기 좋고, XML은 레거시 유지보수에서 여전히 중요합니다.
Jetpack Compose와 XML 차이
핵심은 문법보다 사고방식입니다. XML은 화면 구조와 연결 코드를 분리해서 읽게 만들고, Compose는 상태와 UI를 같은 흐름 안에서 읽게 만듭니다.
표현 방식
XML은 화면 구조를 레이아웃 파일에 두고, 동작은 Activity나 Fragment 코드에서 연결하는 방식입니다. 오래된 안드로이드 코드를 읽다 보면 이 분리가 꽤 익숙하게 느껴집니다.
Compose는 화면도 Kotlin 안에서 설명합니다. 상태가 바뀌면 그 상태에 맞춰 UI를 다시 읽게 만들기 때문에, 며칠만 익숙해지면 흐름이 한곳에 모여 있다는 장점이 분명하게 보입니다.
XML은 뷰를 조립하는 느낌이 강하고, Compose는 상태를 설명해서 화면을 그리게 하는 느낌이 강합니다.
코드 분위기
간단한 버튼도 읽는 방식이 다릅니다.
XML 버튼
<Button
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="로그인" />그리고 코드에서 이벤트를 연결합니다.
binding.loginButton.setOnClickListener {
submitLogin()
}Compose 버튼
Compose에서는 모양과 이벤트가 같은 자리에 옵니다.
@Composable
fun LoginButton(onClick: () -> Unit) {
Button(
onClick = onClick,
modifier = Modifier.fillMaxWidth()
) {
Text(text = "로그인")
}
}핵심은 코드 길이가 아니라 UI와 동작을 어디에서 같이 읽게 하느냐입니다.
상태 감각
제가 Compose를 입문자에게 먼저 권하는 이유도 여기 있습니다. XML + View 방식에서는 상태를 다뤄도 로딩, 버튼 활성화, 텍스트 변경, 리스트 갱신 코드가 쉽게 흩어집니다.
Compose는 질문 순서가 다릅니다. 지금 화면 상태가 무엇인지, 그 상태를 UI에 어떻게 넘길지, 상태가 바뀌면 어떤 부분이 다시 그려질지를 먼저 생각하게 만듭니다.
공식 문서가 말하는 선언형 UI도 결국 이 감각입니다. 뷰를 직접 만지기보다 상태를 바꾸고, UI는 그 상태를 반영하게 두는 방식입니다.
XML에서 자주 겪는 느낌
- 화면 요소를 하나씩 찾아서 바꾼다
- 이벤트 처리 지점이 여러 군데로 흩어진다
- 화면이 커질수록 누가 상태를 바꾸는지 찾기 어려워진다
Compose에서 자주 겪는 느낌
- 먼저 uiState를 정리하게 된다
- 화면이 상태를 받는 함수처럼 읽힌다
- state hoisting이 왜 필요한지 빨리 체감한다
상태 관리 감각을 처음 몸에 익히기에는 Compose 쪽이 더 현대적이고 일관된 편입니다.
상태 변화 예시
프로필 저장 화면을 떠올려보면 차이가 더 선명합니다. 닉네임 입력창, 저장 버튼, 로딩 표시, 완료 메시지가 있는 단순한 화면입니다.
XML 흐름
- EditText에서 값을 읽는다
- 저장을 누르면 로딩 뷰를 보인다
- 결과에 따라 TextView 문구를 바꾼다
- 버튼 활성화 상태도 따로 만진다
이 방식은 익숙하지만, 화면이 커질수록 수정 포인트가 늘어납니다.
Compose 흐름
- ProfileUiState 같은 상태 객체를 둔다
- composable은 상태를 받아 화면을 그린다
- 저장 결과가 바뀌면 상태만 갱신한다
- UI는 바뀐 상태를 기준으로 다시 읽힌다
data class ProfileUiState(
val nickname: String = "",
val isSaving: Boolean = false,
val message: String = ""
)
@Composable
fun ProfileScreen(
state: ProfileUiState,
onNicknameChange: (String) -> Unit,
onSaveClick: () -> Unit
) {
Column {
OutlinedTextField(
value = state.nickname,
onValueChange = onNicknameChange,
label = { Text("닉네임") }
)
Button(
onClick = onSaveClick,
enabled = !state.isSaving
) {
Text(if (state.isSaving) "저장 중..." else "저장")
}
if (state.message.isNotEmpty()) {
Text(text = state.message)
}
}
}현업에서 화면이 복잡해질수록 이 차이는 더 크게 느껴집니다. XML은 뷰 제어 코드가 늘어나기 쉽고, Compose는 상태 모델이 어색하면 그 어색함이 바로 드러납니다.
레거시 현실
여기서 XML을 무시하면 글이 실무와 멀어집니다. 운영 중인 안드로이드 앱은 아직 View 기반 화면이 많고, Activity, Fragment, RecyclerView, custom view, XML 레이아웃이 섞여 있는 구조도 여전히 흔합니다.
그래서 Compose 시대니까 XML은 안 봐도 된다고 배우면 곧바로 벽에 부딪힙니다. 새 화면은 Compose인데 오래된 주문 화면이나 설정 화면은 XML인 경우가 정말 많기 때문입니다.
특히 유지보수에서는 어떤 레이아웃이 어디에 붙는지, ViewBinding으로 무엇이 연결되는지, RecyclerView item이 어떤 XML을 쓰는지 읽어내는 힘이 곧 속도입니다.
상호운용성
Compose와 XML은 당분간 경쟁보다 공존에 가깝습니다. 공식 문서도 Activity의 setContent()나 ComposeView 기반 상호운용성처럼 기존 View 앱에 Compose를 일부만 넣는 흐름을 분명히 안내합니다.
실무에서는 한 번에 갈아엎는 일보다 잘 돌아가는 화면은 두고, 신규 화면이나 복잡한 일부 영역부터 Compose로 바꾸는 경우가 훨씬 많습니다.
Compose를 배운다고 XML을 버리는 게 아니라, XML 프로젝트 안에 Compose를 자연스럽게 끼워 넣는 법까지 알아야 실전 감각이 생깁니다.
XML이 더 자연스러운 장면
- 기존 앱 대부분이 View 기반일 때
- 작은 유지보수와 버그 수정이 우선일 때
- 커스텀 뷰와 기존 레이아웃 자산이 많이 쌓여 있을 때
- 화면 편집 규칙이 이미 XML 중심으로 굳어 있을 때
Compose가 더 자연스러운 장면
- 새 화면을 설계할 때
- 상태 중심으로 화면을 다시 정리하고 싶을 때
- Kotlin 안에서 UI 흐름을 한 번에 읽고 싶을 때
- 화면 단위를 composable로 잘게 나누고 싶을 때
학습 순서
무엇부터 배울지 묻는다면 제 추천은 늘 같습니다. Compose를 먼저 배우고, XML은 읽을 수 있을 정도로 같이 가져가는 방식입니다.
1단계: XML 역할
처음부터 XML 디테일에 오래 머물 필요는 없습니다. XML이 화면 구조를 담는다는 점, ViewBinding이나 findViewById로 코드와 연결된다는 점, ConstraintLayout과 LinearLayout 같은 기본 레이아웃 개념 정도면 출발하기에 충분합니다.
2단계: Compose 사고방식
본격적인 UI 학습은 Compose 중심이 좋습니다. 여기서는 문법보다 흐름을 먼저 잡아야 합니다.
- composable 함수가 무엇인지
- state가 바뀌면 recomposition이 일어난다는 점
- remember와 rememberSaveable 차이
- state hoisting이 왜 필요한지
3단계: ViewModel 연결
Compose를 UI 문법으로만 배우면 금방 한계가 옵니다. 상태를 어디서 만들고 누가 들고 있는지까지 같이 봐야 합니다. 이 부분은 화면 상태는 어디에 두는 게 맞을까 글과도 바로 이어집니다.
4단계: XML 실전 읽기
그다음에는 실제 프로젝트에서 XML을 다시 보는 게 좋습니다. 이때는 옛날 방식이라고 밀어내기보다, View 시스템이 어떤 식으로 역할을 나눴는지 비교하면서 읽으면 훨씬 빨리 익습니다.
누구에게 맞을까
Compose 먼저 추천
- 안드로이드 UI를 처음 배우는 사람
- Kotlin 기반 새 프로젝트로 연습하는 사람
- 상태 관리 감각까지 함께 잡고 싶은 사람
- 함수 단위로 화면을 쪼개는 방식이 잘 맞는 사람
XML도 빨리 읽어야 하는 경우
- 회사나 팀 프로젝트가 View 기반인 사람
- 유지보수 과제가 먼저 잡혀 있는 사람
- Fragment, RecyclerView, 기존 레이아웃 수정을 자주 해야 하는 사람
- 레거시 화면 분석이 곧 실무인 사람
둘 다 같이 보는 게 맞는 경우
- 새 화면은 Compose, 기존 화면은 XML인 환경에 있는 사람
- 점진적 마이그레이션 흐름을 이해해야 하는 사람
- ComposeView 같은 상호운용성을 곧 만나게 될 사람
자주 하는 오해
Compose만 배우면 된다
아닙니다. 새 프로젝트 감각은 좋아질 수 있어도, 기존 앱을 읽는 힘은 XML과 View 구조 이해에서 나옵니다.
XML을 끝내고 넘어가야 한다
이것도 아닙니다. XML 디테일을 너무 오래 붙잡고 있으면 정작 중요한 상태 기반 사고를 늦게 배울 수 있습니다.
Compose가 무조건 더 쉽다
문법은 간결해 보여도 state, recomposition, hoisting은 초반에 꽤 낯섭니다. 다만 이 구간을 넘기면 화면 설계를 더 일관되게 가져가기 쉬운 편입니다.
정리
Jetpack Compose와 XML 차이를 볼 때 중요한 건 우열이 아닙니다. 지금 내가 배우는 단계인지, 유지보수 중인지, 새 화면을 만드는지에 따라 판단 기준이 달라집니다.
- 새로 배우는 UI 축은 Compose로 잡기
- XML은 레거시를 읽는 기본 체력으로 가져가기
- 상태 관리 감각은 Compose에서 의식적으로 훈련하기
- 실무에서는 둘의 상호운용성을 당연한 전제로 받아들이기
관련해서 구조 관점을 더 보고 싶다면 안드로이드 앱 구조를 볼 때 무엇부터 판단해야 할까, 상태 관점은 화면 상태는 어디에 두는 게 맞을까를 이어서 읽어보면 좋습니다.
외부 기준은 공식 문서의 Why adopt Compose, State and Jetpack Compose, Migrate XML Views to Jetpack Compose, Using Compose in Views를 함께 보면 판단 기준이 더 분명해집니다.