Compose 0. Compose 프로젝트에서 처음 만들어지는 MainActivity 얕게 분석하기

ricky_0_k·2021년 11월 26일
0

서론

사실 개인적으로 Compose 를 공부해보려는 시도가 처음은 아니다. 작년 12월에 개인 회고를 하며 Compose 경험하기 를 목표로 잡았었고, 실제 이번 연도 언젠가 stable 버전이 나왔다는 소식을 들어 그 때 Compose 를 살짝 맛보려 했었다.

하지만 업무와 먼저 해야될 일들로 인해 마음먹은 지 1~2일도 안 되어 포기했고, 지금도 벌려놓은 개인 일정들로 인해 Compose 프로젝트를 해볼 생각을 못하고 어느덧 12월을 마주하게 되었다.

그러던 찰나 Android Studio 정식 버전에서 Compose 기반의 프로젝트를 생성할 수 있는 걸 다시 보게 되었다. 12월도 가까워지고 이 내용을 다시 보니 개인의 최소한의 속죄(?)와 양심을 생각해서(...) 기본적으로 생성되는 Compose 프로젝트를 분석해보는 시간이라도 가져야겠다는 생각이 생겨 이 포스트를 작성하게 되었다.

자신감 제로의 상태이지만 열정 만땅으로 일단 시작해본다. 내용을 작성하면 서 코드랩 또한 참고하였다.

접근 및 생성

먼저 프로젝트 생성은 Compose 기반 프로젝트를 선택하면 된다.
이해가 안될수도 있어 사진 한 장을 아래 첨부한다.

그러면 우리는 아래의 코드를 보게 된다.

안녕 Compose (이하 컴포즈)

잠시 기다리면 평소와 같이 MainActivity.kt 코드를 마주하게 된다.
이전과 차이가 있다면, xml 이 없다. (코드는 분석 단계에서 언급할 예정이다.)

갑자기 에러가 발생해요

갑자기 이런 류의 에러가 발생할 수 있다.

앞선 Compose 적용기 포스트에 언급했다시피 컴포즈는 Kotlin version (1.5.31), Android Gradle Plugin (7.0.0), minSdkVersion (21) 을 맞춰주어야 한다. (의심된다면 이 링크를 보라)
필자의 경우에는 gradle JDK 설정과 compileOptions, kotlinOptions 의 Java, JVM 설정을 11로 바꾸었다.

위의 요구사항을 맞추고 빌드에 성공하여 아래 화면을 본다면 성공이다.

조금은 멋 없지만, 예쁘게 하는 건 이 포스트의 목적이 아니므로 바로 분석으로 들어간다.

분석

1. 하나의 언어에서 실행된 파일

일단 기존과 다르게 하나의 언어로만 Activity 가 만들어진다.
그런데 주목할 건 단순히 Activity 에 대해서만 하나의 언어가 아니라는 것이다.

파일을 분석하다보면 ui.Theme 라는 패키지가 보인다.
거기에는 색상값, 테마, 기존의 selector drawable 등 다양한 리소스 대체물들이 보인다.
그리고 실제 아래와 같이 적용하면 우리는 Teal200 색상이 적용된 텍스트를 만날 수 있다.
스크린샷은 따로 넣지 않겠다.

기존에 사용해왔던 xml 지식을 이젠 활용 못한다는 아쉬움(?)이 있지만
Kotlin 순도 100% 로 안드로이드 프로젝트를 만들기 위해선 어쩔 수 없다는 생각이 든다.

2. MainActivity 분석

package kr.co.compose

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import kr.co.compose.ui.theme.ComposeTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeTheme {
                // A surface container using the 'background' color from the theme
                Surface(color = MaterialTheme.colors.background) {
                    Greeting("Android")
                }
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    ComposeTheme {
        Greeting("Android")
    }
}

Activity 에 대한 코드는 이 내용밖에 없다고 했으므로 이 코드를 이제 분석해보자

1. onCreate()

savedInstanceState 를 파라미터로 받는 건 똑같다.
다만 setContentView 가 보이지 않는데 이건 뭘로 대체가 되었는지 대충봐도 알 수 있다.

setContent {
    ComposeTheme {
        // A surface container using the 'background' color from the theme
        Surface(color = MaterialTheme.colors.background) {
            Greeting("Android")
        }
    }
}

// ...
@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!", style = TextStyle(color = Teal200))
}

setContent 안의 내용을 별다른 개념 없이 { ... } 을 "설정한다"로 이해하고,
함수의 개념대로만 이해하면 아래와 같이 이야기할 수 있다.

  1. ComposeTheme 를 설정한다.
  2. MaterialTheme.colors.background 배경 색상을 가지는 Surface 를 설정한다.
  3. Surface 내에 Greeting 를 실행한다.
  4. Greeting 은 Text 를 띄워주는 Composable 어노테이션 함수이다.

확실하게 느낀 건 이 Activity 에 무엇을 설정했고 사용하는지 한 눈에 이해된다는 것이다.
(ComposeTheme 를 설정했고, Text 를 가지고 있으며 텍스트 색상은 Teal200 이다.)
이전의 Kotlin 파일에 1줄짜리 setContentView 를 제공하고, style.xml, activity_main.xml 를 봐야했던 과거와 달리, Kotlin 파일에 View 의 이야기가 코드로 드러난 모습이다.

1. ComposeTheme

타고 들어가면 ui.theme 패키지에서 Theme.kt 에 이미 선언되어 있는 것을 확인할 수 있다.

@Composable
fun ComposeTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) {
    val colors = if (darkTheme) {
        DarkColorPalette
    } else {
        LightColorPalette
    }

    MaterialTheme(
        colors = colors,
        typography = Typography,
        shapes = Shapes,
        content = content
    )
}

@Composable 어노테이션을 가진 함수로 구현되어 있다.

다크 테마 여부를 체크하여 이에 맞는 색상 테마, 폰트 등을 설정하는 것을 확인할 수 있다.
기존의 styles.xml 을 생각하면 편할 것 같다.

2. Surface

Surface 하고 MaterialTheme 는 Google 에서 만든 Material Design 과 관련된 개념이라고 한다.
시각적으로 관련되는 방식 및 그림자를 드리우는 방식 등에 영향을 주며 이것 또한 @Composable 어노테이션을 가진 함수로 구현되어 있다.

3. Greeting 함수

Text 함수를 가지고 있는 @Composable 함수이며 Text 를 설정해주는 것 같이 보인다.
(참고로 Text 또한 @Composable 함수이다.)

여기까지 오면 공통된 단어가 계속 보일 것이다. @Composable 어노테이션을 가진 함수
이 함수의 정체는 무엇일까?

4. @Composable

코드랩에 보면 이런 내용이 있다.

Compose App 은 Composable 함수로 이루어집니다.
Composable 은 다른 Composable 함수를 호출할 수 있는 단순한 함수일 뿐입니다.
새 UI 구성 요소를 만드는 데 필요한 건 함수 뿐이며, Composable 어노테이션은 UI 를 업데이트하고 유지할 수 있도록 Compose 에 지시합니다. Compose 를 사용하면 코드를 작은 뭉치로 구조화할 수 있고, Composable 함수는 줄여서 Composable 이라고도 합니다.

정리해보면 Composable 어노테이션 함수는 UI 이다.
그러면서 반환값이 없기 때문에, UI 값을 리턴 받아 다른 위치에서 활용될 가능성은 없다.
그 개념으로 이해를 해보면 Greeting 함수는 일종의 UI이며, Greeting 내에서는 Text UI 를 가진다고 표현할 수 있다.

5. 결론

우리는 위 onCreate() 분석을 통해, Composable 함수를 선언하여 UI 를 설정하고, UI 와 관련된 설정 (ex. 텍스트 색상 설정) 은 오로지 Composable 함수 선언부에서만 이루어진다는 것을 확인할 수 있다.

2. @preview

onCreate 밑을 보면 이런 함수가 있다.

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    ComposeTheme {
        Greeting("Android")
    }
}

내용을 보면 Composable 함수인데, onCreate 의 setContent 와 비슷한 내용이 적혀있다.
왜 사용하는 걸까? 정의를 보면 바로 알 수 있다.

@PreView 는 Composable 함수에 적용할 수 있으며, Android Studio Preview 를 보여주기 위해 활용됩니다.

정리하면 IDE 에서 UI 를 보기 위해 사용된다. 실제 보면 아래 우측과 같이 preview 를 볼 수 있다.

실험

1. 중복 없애기

개인적으로 코드가 비슷하고 중복을 없애도 문제없을 것 같아 보인다.
우리가 배운 Composable 개념을 활용해 이렇게 하면 안될까?

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MainView()
        }
    }
}

@Composable
fun MainView() {
    ComposeTheme {
        // A surface container using the 'background' color from the theme
        Surface(color = MaterialTheme.colors.background) {
            Greeting("Android")
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!", style = TextStyle(color = Teal200))
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    MainView()
}

MainView 라는 Composable 함수를 만들어 setContent 와 Preview 에서 사용되는 중복 코드를 줄였다. 이렇게 해도 결과는 동일하다.

2. Preview 여러개 선언하기

PreView 를 여러개 사용할 수 있다.
아래와 같이 DefaultPreView2 를 선언하고 빌드하면 PreView 가 2개 보인다.

결론

오직 MainActivity 만 얕게 분석해보았고 이에 대한 실험만 해보았는데도 Compose 의 장점은 명확히 보였다.

  1. 100% 에 가까워진 Kotlin 코드
  2. 선언형 UI 로 인한 직관성 향상
  3. UI 의 반환값이 없어 코드 수정으로 인한 영향 최소화

여기에 MVVM 이나 MVP 를 적용시킨다면 xml 에 kotlin 을 강제 적용하여 고통받았던 에피소드는 이제 없어질 것이다. (함수형 변수 넣을 때 맨날 고통받았던 기억....) 더불어 View 가 수동적이 되어야 하는 MVVM 의 경우엔, 3번 요인으로 인해 더욱 아키텍처 개념을 확고하게 할 수 있을 것 같다.

장점을 보고 관련된 레퍼런스들을 깊게 deep Dive 해야겠다는 생각이 들었던 분석 시간이었다.

참고 ( + 깊게 분석해볼 레퍼런스)

  1. 안드로이드 Jetpack Compose! 구글 Codelabs을 통해 알아본다.
  2. 코드랩
profile
valuable 을 추구하려 노력하는 개발자

0개의 댓글