안녕하세요!
Android Compose 가 2021년 2월 24일 Beta 를 시작으로 2022년 7월 28일 드디어 정식으로 배포 되었습니다.
Android 개발자로 먹고 살고 있음에도 불구하고 이제서야 Compose 를 사용해보게 되었네요.
권장사항
Compose 를 사용하기 위해선 Android Studio Arctic Fox | 2020.3.1 이상 버전에서 작업할 것을 권장하고 있는데요,
그냥 지금 2022년 11월 기준으로 최신 버전을 받으시면 되실겁니다.
Jetpack Compose 가 뭐죠?
Compose 는 기존 Layout XML UI 를 대신 하는 선언형 프로그래밍 UI 도구 키트 입니다.
Compose 이해 | Jetpack Compose | Android Developers
Compose 이해 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Jetpack Compose는 Android를 위한 현대적인 선언형 UI 도구 키트입니다. Compose는 프런트엔드 뷰를 명령
developer.android.com
아마 위의 설명으로는 잘 이해가 가지 않으실 텐데요
제가 이해를 돕기 위해 간단한 샘플을 하나 만들어 보겠습니다.
각각 Android Layout XML UI 와 Compose 를 사용한 예제를 만들어 보겠습니다.
둘 다 간단한 물고기 이름을 보여주는 간단한 리스트뷰만 구현해 보았습니다.
Android Layout XML UI (기존 방법)
https://github.com/areemak/imperative-programming-sample-android
GitHub - areemak/imperative-programming-sample-android: 명령형 프로그래밍을 설명하기 위해 사용된 샘플 입니
명령형 프로그래밍을 설명하기 위해 사용된 샘플 입니다. Contribute to areemak/imperative-programming-sample-android development by creating an account on GitHub.
github.com
MainActivity.java
public class MainActivity extends AppCompatActivity {
private MainListAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 데이터 생성
List<String> data = makeData();
// adapter 생성
mAdapter = new MainListAdapter(MainActivity.this, data);
// RecyclerView 초기화
RecyclerView rcvMain = findViewById(R.id.rcv_main);
rcvMain.setAdapter(mAdapter);
rcvMain.setLayoutManager(new LinearLayoutManager(MainActivity.this));
}
private List<String> makeData() {
String[] _data = {"꽁치", "갈치", "고등어", "삼치", "멸치", "참치", "쥐치", "기타등등"};
return Arrays.asList(_data);
}
static class MainListAdapter extends RecyclerView.Adapter<MainListAdapter.ViewHolder> {
private Context mContext;
private List<String> mData;
public MainListAdapter(Context mContext, List<String> mData) {
this.mContext = mContext;
this.mData = mData;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.layout_main_list_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.bindView(mData.get(position));
}
@Override
public int getItemCount() {
return mData.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvLabel;
public ViewHolder(@NonNull View itemView) {
super(itemView);
tvLabel = itemView.findViewById(R.id.tv_label);
}
public void bindView(String data) {
tvLabel.setText(data);
}
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rcv_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
layout_main_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
tools:text="데이터"/>
</LinearLayout>
결과
Compose UI (선언형 프로그래밍)
https://github.com/areemak/declarative-programming-sample-android/
GitHub - areemak/declarative-programming-sample-android: 선언형 프로그래밍을 설명하기 위해 사용된 샘플 입
선언형 프로그래밍을 설명하기 위해 사용된 샘플 입니다. Contribute to areemak/declarative-programming-sample-android development by creating an account on GitHub.
github.com
MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeTestAndroidTheme {
Surface(
modifier = Modifier.fillMaxSize(),
) {
val data: List<String> = makeData()
ListView(data)
}
}
}
}
private fun makeData(): List<String> {
val _data: Array<String> = arrayOf(
"꽁치",
"갈치",
"고등어",
"삼치",
"멸치",
"참치",
"쥐치",
"기타등등",
)
return _data.toList()
}
}
결과
차이점이 보이시나요?
일단 코드량이 압도적으로 차이가 나죠..
코딩 시간도 기존꺼는 15분 정도 걸렸는데 Compose 는 2분 걸렸습니다. 2분.
이렇게 차이가 날 수 있는 이유가 바로 선언형 프로그래밍의 장점이라고 할 수 있습니다.
기존에는 리스트 뷰를 보여주기 위해서 아래와 같은 절차들이 필요했습니다.
- xml 에 RecyclerView 를 선언해준다.
- 코드에서 RecyclerView 를 binding 시킨다.
- Adapter 를 생성하고 list item 을 보여줄 layout 도 하나 생성한다.
- list item layout 을 inflate 한다.
- ViewHolder 를 생성하고 데이터와 list item layout 을 binding 시킨다.
- 데이터를 생성하여 RecyclerView 와 adapter 를 연결시킨다.
이런걸 명령형 프로그래밍이라고 합니다. 일단 미리 다 만들어 놔야지만 화면에 보여지는게 가능한 방식이죠.
정말 View 하나 추가할 때마다의 그 답답함은 Android 개발자 분들이라면 다 아실거라 생각합니다..

Compose 를 보시면
- list item Composer 를 생성한다.
- list item 을 보여줄 list composer 를 생성한다.
- 데이터를 list composer 로 넘겨준다.
명령형 프로그래밍 보다 할일이 많이 줄어들었죠.
선언형 프로그래밍은 과정보단 결과물을 더 중요시 하기 때문입니다.
이게 무슨 소리냐면, 개발자는 그냥 결과물 데이터와 list item view 구현에만 집중하고 나머지 listView 와 데이터의 연결, 반복 등은 신경쓰지 않아도 된다는 거예요.
이 방식은 요즘 많이 사용되는 React, Flutter, SwiftUI(iOS) 에도 적용되어 있습니다. 곧 선언형 프로그래밍이 대세라는 것이죠.
아직까지는 Compose 를 많이 다루어 보지 않아서 그런지 장점밖에 보이지 않네요..
추후에 Compose 를 사용하여 간단한 앱을 하나 만들어 보며 장점과 단점을 포스팅 해 보겠습니다.