
Jetpack Compose는 네이티브 UI를 빌드하기 위한 최신 도구 키트이다. 빠르고 간소화된 개발에 도움을 준다. 반응형 프로그래밍 모델과 Kotlin의 간결함, 용이성을 결합한다. 데이터를 UI 계층 구조로 변환하는 함수를 호출하여 UI를 표현한다. 데이터가 변경되면 프레임워크는 자동으로 다시 실행하여 UI 계층 구조를 갱신한다.
재사용 가능한 Composable을 만들어 UI 요소 라이브러리를 보다 쉽게 구축할 수 있다.
Compose를 사용하면 Activity가 Android 앱의 진입점이 된다. 일반적으로 프로젝트를 생성하면 MainActivity가 생성되는데 setContent를 사용하여 Layout을 정의하지만 기존의 View 시스템과 처럼 XML 파일을 사용하는 대신 파일 내에서 Composable 함수를 호출한다.
UI에 더 많은 구성 요소가 추가될수록 더 많이 중첩된다. 함수의 크기가 커짐에 따라 가독성에 영향을 줄 수 있다. 재사용 가능한 작은 구성요소를 만들어 앱에서 사용되는 UI 요소 라이브러리를 쉽게 구축할 수 있다. 각 요소들은 화면의 작은 부분을 담당해 독립적으로 관리할 수 있다.
인사말이 포함된 MyApp() Composable을 예시로 만들어 보겠다.
@Composable
private fun MyApp() {
Surface(color = MaterialTheme.colors.background) {
Greeting("Android")
}
}
MyApp() Composable을 재사용하면 코드 중복을 방지할 수 있으므로 onCreate() 콜백과 Preview를 정리할 수 있다.
MainActivity.kt파일은 아래 코드와 같아야 한다.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
JetpackComposeTutorialTheme {
MyApp()
}
}
}
}
@Composable
private fun MyApp() {
Surface(color = MaterialTheme.colors.background) {
Greeting("Android")
}
}
@Composable
private fun Greeting(name: String) {
Surface(color = MaterialTheme.colors.primary) {
Text(text = "Hello $name!", modifier = Modifier.padding(24.dp))
}
}
@Preview(showBackground = true)
@Composable
private fun DefaultPreview() {
BasicsCodelabTheme {
MyApp()
}
}
위의 코드로 Preview를 보면 아래와 같은 결과가 나온다.

Compose의 3가지 기본 표준 레이아웃 요소는 아래 그림과 같다.

Composable 콘텐츠를 가지는 Composable 함수이므로 내부에 item을 배치할 수 있다. Column의 경우 열 형태 즉, 세로로 배치된다.
Column을 사용해보기 위해 위에서 만든 Greeting() 함수를 변경해보겠다.
@Composable
fun Greeting(name: String) {
Surface(color = MaterialTheme.colors.primary) {
Column(modifier = Modifier.padding(24.dp)) {
Text(text = "Hello, ")
Text(text = name)
}
}
}
Composable 함수는 다른 함수들처럼 사용할 수 있다. 이 점은 UI 구축을 강력하게 만들어 준다. 그 이유는 UI가 표시되는 방식에 영향을 주는 실행문을 추가할 수 있기 때문이다.
for 반복문을 MyApp() 함수에 적용 시켜본다면,
@Composable
private fun MyApp(names: List<String> = listOf("World", "Compose")) {
Column {
for (name in names)
Greeting(name = name)
}
}

크기 수정이나 제약 조건을 건드리지 않았기 때문에 각 요소들은 최소한의 공간을 차지한다. 만약 디스플레이의 너비를 320dp로 조정한다면 어떤 결과가 나올지 살펴보겠다.
@Preview(showBackground = true, widthDp = 320)
@Composable
private fun DefaultPreview() {
JetpackComposeTutorialTheme {
MyApp()
}
}

Modifier는 Compose에서 자주 사용된다.fillMaxWidth와 padding을 이용해 화면을 채우고 요소 간의 간격을 만들어보겠다.
@Composable
private fun MyApp(names: List<String> = listOf("World", "Compose")) {
// 측면과의 거리를 위해 padding 추가
Column(modifier = Modifier.padding(8.dp)) {
for (name in names)
Greeting(name = name)
}
}
@Composable
fun Greeting(name: String) {
Surface(
color = MaterialTheme.colors.primary,
modifier = Modifier.padding(vertical = 8.dp)
) {
Column(modifier = Modifier.fillMaxWidth().padding(24.dp)) {
Text(text = "Hello, ")
Text(text = name)
}
}
}

Modifier는 오버로드를 가질 수 있다. 구성 요소에 여러 Modifier를 추가하려면 위 코드와 같이 간단히 연결이 된다.
Button은 Composable을 마지막 인수(Argument)로 취하는 Composable이다. 모든 콘텐츠가 Button의 자식으로 추가될 수 있다.
Compose는 머티리얼 디자인 버튼 사양(
Button,OutlinedButton및TextButton)에 따라 다양한 형태를 제공한다.Text를Button콘텐츠로 감싸려면OutlinedButton을 사용해야 한다.
Row 끝에 Composable을 배치하는 방법을 알아야 한다. alignEnd modifier가 없기 때문에 Composable에 weight(가중치)를 부여하는 방법으로 대신한다. weight modifier는 요소가 사용가능한 모든 공간을 채우도록 하여 가중치가 없는 다른 요소를 밀어낸다. 또한, fillMaxWidth modifier를 중복으로 만든다.
아래는 각 요소 안에 Button을 넣은 코드와 실행 결과다.
@Composable
private fun Greeting(name: String) {
Surface(
color = MaterialTheme.colors.primary,
modifier = Modifier.padding(vertical = 8.dp)
) {
Row(modifier = Modifier.padding(24.dp)) {
Column(modifier = Modifier.weight(1f)) {
Text(text = "Hello, ")
Text(text = name)
}
OutlinedButton(onClick = { /*TODO*/ }) {
Text("Show more")
}
}
}
}
