[MindSync] Jetpack Compose로 마이그레이션

hegleB·2024년 1월 15일
1
post-thumbnail

Jetpack Compose 마이그레이션 한 이유

  • XML을 사용하지 않고 Kotlin Code만으로 UI 개발이 가능하다
  • 복잡한 RecyclerView 및 Adapter를 더 이상 사용하지 않아도 된다

Jetpack Compose 마이그레이션 전략

작은 컴포넌트부터 시작하기

  • TopAppBar, Image, Button 등 작은 컴포넌트를 우선으로 먼저 Compose로 변환 시킨다

XML과 Compose 공존하기

  • XML에서 ComposeView를 사용하여 Compose로 작성된 컴포넌트를 사용하여 기존 XML 뷰 시스템과 같이 사용할 수 있도록 한다

작은 것부터 시작해서 점차적으로 Compose로 변환 시키기

  • View -> ViewGroup -> RootView로 점차적으로 Compose로 변환 시켜 전체적으로 Compose로 사용할 수 있게 한다.

Compose 환경

Gradle 설정하기

android {
	buildFeatures {
    	compose = true
    }
    
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.7"
    }
}

build.gradle.kts 파일에서 buildFeatures를 통해 compose를 활성화 시키고
composeOptions에서 kotlinCompilerExtensionVersion 옵션을 설정하여 Compose와 함께 사용할 Kotlin 컴파일러 플러그인의 버전을 지정한다.

dependencies {
	    // compose
    val composePlatform = platform("androidx.compose:compose-bom:2023.10.01")
    implementation(composePlatform)
}

Bill of Materials(BOM) 방식으로 Compose 관련 의존성들을 관리한다. 여기서 "androidx.compose:compose-bom:2023.10.01"는 Compose 관련 모든 라이브러리들이 2023.10.01 버전과 호환되도록 한다

dependencies {
    implementation("androidx.compose.material3:material3")
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.ui:ui-graphics")
    implementation("androidx.compose.ui:ui-tooling-preview")
    debugImplementation("androidx.compose.ui:ui-tooling")
}

Material Design 3 UI 컴포넌트의 사용을 위한 설정과 함께, 기본적인 UI 기능, 그래픽 처리 기능을 제공하는 라이브러리를 포함하였다. 또한, UI 미리보기를 지원하는 도구와 개발 및 디버깅 과정에서 필요한 Jetpack Compose 관련 도구들을 프로젝트에 추가하였다.

dependencies {
    implementation("androidx.activity:activity-compose:1.8.2")
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
    implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.2")
    implementation("androidx.navigation:navigation-compose:2.7.6")
    implementation("androidx.constraintlayout:constraintlayout-compose:1.0.1")
  
}

compose용 viewmodel, navigation, constraintlayout 라이브러리를 사용하여 기존에 작성한 코드에 맞게끔 적용할 것이다.

Compose용 디자인 시스템 구축

XML Resources를 Compose System으로 재정의

colors.xml

<resources>
    <color name="black">#222227</color>
    <color name="gray6">#333232</color>
    <color name="gray5">#6A6A6A</color>
    <color name="gray4">#949494</color>
    <color name="gray3">#D9D9D9</color>
    <color name="gray2">#F4F4F4</color>
    <color name="gray1">#F9F9F9</color>
	...
</resources>

Color.kt

val Black = Color(0xFF222227)
val Gray6 = Color(0xFF333232)
val Gray5 = Color(0xFF6A6A6A)
val Gray4 = Color(0xFF949494)
val Gray3 = Color(0xFFD9D9D9)
val Gray2 = Color(0xFFF4F4F4)
val Gray1 = Color(0xFFF9F9F9)
...

XML 기반의 안드로이드 UI 개발에서는 colors.xml 파일을 통해 앱 전체에서 사용할 색상을 정의하고 관리한다. Jetpack Compose로 마이그레이션할 때는 이러한 색상 정의를 코틀린 코드로 옮겨 Color 객체로 변환해야 한다.
XML의 태그에 정의된 색상 값을 Jetpack Compose에서 사용할 수 있도록 코틀린 파일 내에 Color 객체로 변환하고 있다. 각 XML 색상 값의 16진 코드 앞에 0xFF를 붙여 전체 색상 값으로 만든 후, Color 클래스의 생성자에 전달하여 Compose용 색상 객체를 생성한다.

typography.xml

<resources>
    <style name="Typography">
        <item name="android:textColor">@color/black</item>
        <item name="android:includeFontPadding">false</item>
    </style>

    <style name="Typography.Title01" parent="Typography">
        <item name="fontFamily">@font/pretendard_semi_bold</item>
        <item name="android:textSize">24sp</item>
    </style>

    <style name="Typography.Title02" parent="Typography">
        <item name="fontFamily">@font/pretendard_semi_bold</item>
        <item name="android:textSize">20sp</item>
    </style>

    <style name="Typography.Title03" parent="Typography">
        <item name="fontFamily">@font/pretendard_semi_bold</item>
        <item name="android:textSize">18sp</item>
    </style>

    <style name="Typography.Body01.Medium" parent="Typography">
        <item name="fontFamily">@font/pretendard_medium</item>
        <item name="android:textSize">16sp</item>
    </style>
    ...
 </resources>

Type.kt

val PretendardSemiBold = FontFamily(Font(R.font.pretendard_semi_bold))
val PretendardMedium = FontFamily(Font(R.font.pretendard_medium))
val PretendardRegular = FontFamily(Font(R.font.pretendard_regular))
val PretendardLight = FontFamily(Font(R.font.pretendard_light))

val defaultTextStyle = TextStyle(
    platformStyle = PlatformTextStyle(
        includeFontPadding = false,
    ),
)

val Typography = Typography(
    displayLarge = defaultTextStyle.copy(
        fontFamily = PretendardSemiBold,
        fontSize = 24.sp,
    ),
    displayMedium = defaultTextStyle.copy(
        fontFamily = PretendardSemiBold,
        fontSize = 20.sp,
    ),
    displaySmall = defaultTextStyle.copy(
        fontFamily = PretendardSemiBold,
        fontSize = 18.sp,
    ),
    headlineLarge = defaultTextStyle.copy(
        fontFamily = PretendardMedium,
        fontSize = 16.sp,
    ),
    ...
 )

Typography를 코드로 직접 정의하여 UI의 텍스트 스타일링을 관리한다. XML 방식에서 typography.xml에 정의된 스타일을 Compose로 마이그레이션하려면, 각 스타일을 TextStyle 객체로 변환해야한다.
typography.xml 파일에 정의된 텍스트 스타일은 style 태그를 사용하여 구성된다. 이러한 스타일들은 텍스트의 색상, 패딩, 글꼴, 텍스트 크기 등을 지정한다. 마이그레이션을 진행할 때, 각 XML 스타일 속성을 Jetpack Compose의 TextStyle 객체와 Typography 클래스로 변환해야한다.
Typography 클래스의 파라미터에 맞게 텍스트 스타일을 지정하였다.

themes.xml

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Base.Theme.MindSync" parent="Theme.Material3.DayNight.NoActionBar">
        <!-- Customize your light theme here. -->
        <!-- <item name="colorPrimary">@color/my_light_primary</item> -->
        <item name="android:windowLightStatusBar">true</item>
        <item name="android:statusBarColor">@color/white</item>
    </style>

    <style name="Theme.MindSync" parent="Base.Theme.MindSync" />
</resources>

Theme.kt

private val DarkColorScheme = darkColorScheme(
    primary = Gray6,
    secondary = Gray4,
    tertiary = Gray5,
)

private val LightColorScheme = lightColorScheme()

@Composable
fun MindSyncTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    dynamicColor: Boolean = true,
    content: @Composable () -> Unit,
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }

        darkTheme -> DarkColorScheme
        else -> LightColorScheme
    }
    val view = LocalView.current
    if (!view.isInEditMode) {
        SideEffect {
            val window = (view.context as Activity).window
            window.statusBarColor = Color.White.toArgb()
            WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = true
        }
    }

    MaterialTheme(
        colorScheme = colorScheme,
        typography = Typography,
        content = content,
    )
}

XML에서는 themes.xml을 통해 앱의 테마와 스타일을 정의하지만, Compose에서는 코틀린 코드 내에서 테마를 직접 구성한다.
먼저 색상 스키마 정의한다. DarkColorScheme 및 LightColorScheme 객체를 생성하여 Compose 테마의 어두운 모드와 밝은 모드의 색상을 정의한다. 이 객체들은 primary, secondary, tertiary 등의 색상을 설정한다.
MindSyncTheme 컴포저블 함수를 정의하여 앱의 테마를 설정한다. 이 함수는 darkTheme과 dynamicColor 플래그를 인자로 받아 시스템 테마 설정에 따라 어두운 테마 또는 밝은 테마를 적용한다.

마이그레이션 결과

XMLJetapckCompose

XML과 JetpackCompose의 GPU 렌더링을 분석한 결과, XML로 UI를 구현할 때 더 많은 명령어를 사용하기 때문에 처리 시간이 더 오래 걸린다는 것을 확인할 수 있었다.

참고자료

https://developer.android.com/jetpack/compose/interop/migration-strategy?hl=ko

https://developer.android.com/topic/performance/rendering/inspect-gpu-rendering?hl=ko

profile
성장하는 개발자

0개의 댓글

관련 채용 정보