본문 바로가기
안드로이드/코틀린

안드로이드 jetpack compose 공부 - 1

by 나이아카 2022. 3. 1.

 안드로이드 스튜디오의 새로운 UI 개발 프레임워크인 jetpack compose는 서서히 고개를 들고 있습니다. 어느덧 요구하는 회사들이 생겨나기 시작한 것으로 보아 빠른 시일 내에 유행처럼 번질 것 같습니다. 그래서 저도 jetpack compose에 대해서 공부해보려고 합니다.

 그러나 현재 사용중인 프로젝트에서 compose를 적용하기에는 아직 제 숙련도가 매우 낮기 때문에 기본적으로 생성할 수 있는 empty compose activity 예제를 이용해서 익숙해져보려고 합니다.


 프로젝트 생성하고 나서 제일 먼저 해야할 일은 gradle을 만지는 것입니다. 사실 최신버전 gradle과 android studio를 사용하면 생성 후, 별 문제 없이 사용할 수 있지만, 기존 프로젝트에 사용하거나 낮은 버전을 이용하게 되면 gradle을 올리면 이것저것 플러그인 관련 문제가 많이 생기는 듯 합니다.

//Build.gradle(Module)

dependencies {
    //jetpack compose를 위한 기본 구성
    implementation "androidx.compose.ui:ui:1.2.0-alpha03"
    implementation "androidx.activity:activity-compose:1.5.0-alpha02"
    implementation "androidx.compose.material:material:1.2.0-alpha03"
    implementation "androidx.compose.ui:ui-tooling:1.2.0-alpha03"
    
    //다른 라이브러리들
}

 jetpack compose는 아무래도 기본 안드로이드 프레임워크에 속하지 않았다보니 gradle에서 추가적으로 다운을 받아주어야 합니다. 위와 같이 라이브러리를 추가하면 이제 jetpack compose를 사용할 준비가 완료됩니다.

 하지만 이때 compileSdkVersion와 targetSdkVersion가 30이하일 때 build 에러가 발생할 수 있습니다. 이때 두 버전을 31로 변경해주는 경우 문제 없이 실행될 수 있습니다.(다른 에러가 발생할 수 있을 것 같은데 저는 발생하지 않아서 확인할 수가 없었습니다.)

 제일 먼저 뜨는 에러는 some kotlin libraries attached to this project were compiled with a newer kotlin compiler and can't be read. Please update kotlin plugin. 입니다.

 이를 해결하기 위해서 gradle 버전을 상승시켰더니 This version of the Android Support plugin for IntelliJ IDEA (or Android Studio) cannot open this project, please retry with version 2021.1.1 or newer. 에러가 발생하더군요.

 결국 check for updates로도 안드로이드 최신 버전 설치가 안되서 안드로이드 스튜디오를 지운 다음 다시 깔았습니다.

 

buildscript {
    ext.kotlin_version = "1.5.31"
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:7.3.0-alpha03'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

 또한 Build.gradle을 위와 같이 버전업해서 작성했더니 그제서야 jetpack compose가 열리더군요!(역시 환경설정이 제일 빡센 거 같습니다.)

 결국 jetpack compose를 시작하기 위해선 최신 버전의 gradle과 android studio가 필요합니다. 아래부터는 기본적으로 작성된 empty compose activity 코드를 살펴보겠습니다.

empty compose activity 기본 생성 코드

 

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import com.jj.jetpackcomposepractice.ui.theme.JetpackComposePracticeTheme

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

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

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

 위의 코드는 MainAcitivty 코드 입니다. 여기서 봐야할 제일 중요한 부분은 기존에 setContentVIew로 이루어져 xml 코드와 연결된 부분이 setContent로 바뀌었다는 점입니다. setContent는 JetpackComposePracticetheme로 이루어져 있는데, 이 코드는 아래 Theme 코드에 보면 확인할 수 있습니다. 이 Theme는 머테리얼을 기준으로 작성되고 있어서 기존에 구글의 머테리얼을 이용해서 코드를 작성했다면 각각의 기능에 대한 이해도가 높을 것 같습니다. 저는 아무래도 머테리얼 관련 지식이 없어 바로 눈에 확 들어오지는 않네요.

 그 아래에 있는 @Composable 어노테이션의 Greeting 함수는 직접적인 UI를 담당하고 있는 함수입니다. 먼저 저 코드를 이해하려면 Composable 어노테이션에 대해서 이해해야 하는데요.

 Composable 어노테이션이 붙은 함수는 jetpack compose의 가장 기본이 되는 widget의 역할을 하게 됩니다. 이 안에서는 Text나 Box, Layout과 같은 compose의 UI 관련 코드들을 삽입하게 됩니다. 기존의 xml에서 사용하던 각 버튼이나 TextView와 같은 친구들이 앱의 상황에 맞는 디자인(color 및 background 등)을 가진 채 일반화되었다고 생각할 수 있겠습니다.

 그 아래에서 주목해야할 것은 Preview 어노테이션입니다. 이 어노테이션은 기존 xml에서 코드 화면 옆에서 볼 수 있던 미리보기 화면과 같은 동작을 위한 어노테이션입니다. composable 어노테이션의 위에 있어야 동작하는데, 코드의 ui는 대부분 activity의 setcontent를 이용해 작성될 것이기 때문에 확인을 위해서는 새로운 코드(예제에서는 DefaultPreview 코드)가 필요합니다. 이를 위해서 기존의 test code를 작성하는 것처럼 UI preview 코드를 작성할 수 있습니다.

 

import androidx.compose.ui.graphics.Color

val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5)

 위의 코드는 Color 코드입니다. 딱히 특별한 것은 없고 Color 라는 클래스가 compose 버전으로 새로 등장했습니다. 이는 기존의 color.xml에서 사용하던 것들을 코틀린 내부로 옮겨온 것으로 받아들일 수 있겠습니다.

import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes
import androidx.compose.ui.unit.dp

val Shapes = Shapes(
    small = RoundedCornerShape(4.dp),
    medium = RoundedCornerShape(4.dp),
    large = RoundedCornerShape(0.dp)
)

 위의 코드는 Shape 코드입니다. 여기서 만들어진 코드는 Theme 내에서 파라미터를 지정해주는데 사용됩니다. roundedCornerShape는 이름답게 둥근 모서리를 만들어주는 코드입니다.

class Shapes(
    /**
     * Shape used by small components like [Button] or [Snackbar]. Components like
     * [FloatingActionButton], [ExtendedFloatingActionButton] use this shape, but override
     * the corner size to be 50%. [TextField] uses this shape with overriding the bottom corners
     * to zero.
     */
    val small: CornerBasedShape = RoundedCornerShape(4.dp),
    /**
     * Shape used by medium components like [Card] or [AlertDialog].
     */
    val medium: CornerBasedShape = RoundedCornerShape(4.dp),
    /**
     * Shape used by large components like [ModalDrawer] or [ModalBottomSheetLayout].
     */
    val large: CornerBasedShape = RoundedCornerShape(0.dp)
)

 각 파라미터가 무슨 의미를 지녔는지 궁금해서 찾아보았습니다. 먼저 small은 Button이나 Snackbar와 같은 컴포넌트에서 사용되고, medium에 작성한 Shape는 Card나 AlertDialog에 적용됩니다. 마지막으로 large에 작성한 Shape는 ModlDrawer나 ModalBottomSheetLayout에서 사용된다고 합니다.

 

import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp

// Set of Material typography styles to start with
val Typography = Typography(
    body1 = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 16.sp
    )
    /* Other default text styles to override
    button = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.W500,
        fontSize = 14.sp
    ),
    caption = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 12.sp
    )
    */
)

위의 코드는 Type 코드입니다. Typography를 작성하는데, 여기서는 각 위젯의 스타일에 관한 것들을 작성해줄 수 있고, 이는 나중에 언급될 Theme에 적용하게 되면 이 테마가 적용된 앱의 모든 ui에 기본적으로 작성됩니다.

 

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable

private val DarkColorPalette = darkColors(
    primary = Purple200,
    primaryVariant = Purple700,
    secondary = Teal200
)

private val LightColorPalette = lightColors(
    primary = Purple500,
    primaryVariant = Purple700,
    secondary = Teal200

    /* Other default colors to override
    background = Color.White,
    surface = Color.White,
    onPrimary = Color.White,
    onSecondary = Color.Black,
    onBackground = Color.Black,
    onSurface = Color.Black,
    */
)

//이 메소드의 이름은 기본적으로 project name + Theme로 이루어집니다.
@Composable
fun JetpackComposePracticeTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
    val colors = if (darkTheme) {
        DarkColorPalette
    } else {
        LightColorPalette
    }

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

 위의 코드는 Theme 코드입니다. 위에서 여러번 언급된 Theme입니다. 기본적으로 MaterialTheme를 사용하며, 위에서 설명된 Shape와 Typography를 사용합니다. 또한 colors 테마가 dark인지 아닌지에 따라 color를 지정해주는 코드도 예제로 제공합니다.(예제를 지정해주는 것으로 보아 구글에서 compose를 이용한 앱 dark 테마를 이런식으로 작성하기를 원하는 것 같습니다.)

 이 코드에서 작성한 테마가 이 앱의 기본적인 테마가 됩니다. xml을 이용한 ui작성시에 theme.xml로 작성했던 것들을 코틀린 코트로 옮겨온 것으로 보입니다.


 각 이름들의 코틀린 파일들은 기존의 xml로 작성되던 것들을 코틀린으로 옮겨오는 방법을 설명해주는 듯 한 느낌이 듭니다. 좀 더 직관적인 코드로 인해 익숙해지면 좀 더 편하게 사용할 수 있지 않을까 하는 생각이 들기는 합니다만, 아직까지는 xml이 더 익숙하다보니 더 공부가 필요한 듯 합니다.

댓글