제품 뉴스

Jetpack Compose 2025년 12월 출시의 새로운 기능

전문 길이: 6분
월 3일
Nick Butcher
제품 관리자

오늘 Jetpack Compose 2025년 12월 출시가 안정화되었습니다. 여기에는 핵심 Compose 모듈 버전 1.10과 Material 3 버전 1.4가 포함되어 있으며 (전체 BOM 매핑 참고), 새로운 기능과 주요 성능 개선사항이 추가되었습니다.

오늘 출시 버전을 사용하려면 Compose BOM 버전을 2025.12.00으로 업그레이드하세요.

implementation(platform("androidx.compose:compose-bom:2025.12.00"))

성능 개선사항

Google은 앱의 런타임 성능이 개발자와 사용자에게 매우 중요하다는 것을 알고 있으므로 성능은 Compose팀의 주요 우선순위였습니다. 이번 출시에는 여러 개선사항이 포함되어 있으며 최신 버전으로 업그레이드하기만 하면 모든 개선사항을 이용할 수 있습니다. Google의 내부 스크롤 벤치마크에 따르면 이제 Compose는 뷰를 사용하는 경우와 동일한 성능을 제공합니다.

janky.png

Compose의 여러 버전에서 뷰와 Jetpack Compose를 비교하는 스크롤 성능 벤치마크

지연 프리페치의 일시중지 가능한 구성

지연 프리페치의 일시중지 가능한 구성이 이제 기본적으로 사용 설정됩니다. 이는 Compose 런타임이 작업을 예약하는 방식을 근본적으로 변경한 것으로, UI 워크로드가 많은 동안 버벅거림을 크게 줄이도록 설계되었습니다.

이전에는 구성이 시작되면 완료될 때까지 실행해야 했습니다. 구성이 복잡한 경우 기본 스레드를 단일 프레임보다 오래 차단하여 UI가 정지될 수 있습니다. 일시중지 가능한 구성을 사용하면 런타임이 시간이 부족한 경우 작업을 '일시중지'하고 다음 프레임에서 작업을 재개할 수 있습니다. 이는 지연 레이아웃 프리페치와 함께 사용하여 프레임을 미리 준비할 때 특히 효과적입니다. Compose 1.9에 도입된 지연 레이아웃 CacheWindow API는 더 많은 콘텐츠를 미리 가져오고 일시중지 가능한 컴포지션의 이점을 활용하여 훨씬 더 원활한 UI 성능을 제공하는 좋은 방법입니다.

pausable.gif

일시중지 가능한 컴포지션과 지연 프리페치를 결합하면 버벅거림을 줄일 수 있습니다.

또한 Modifier.onPlaced, Modifier.onVisibilityChanged 및 기타 수정자 구현을 개선하여 다른 곳에서도 성능을 최적화했습니다. Google은 Compose의 성능 개선에 계속 투자할 예정입니다.

새로운 기능

유지

Compose는 다양한 수명 주기에서 상태를 유지하고 관리하기 위한 여러 API를 제공합니다. 예를 들어 remember는 구성 전반에 상태를 유지하고 rememberSavable/rememberSerializable은 활동 또는 프로세스 재작성 전반에 상태를 유지합니다.retain은 이러한 API 사이에 있는 새로운 API로, 직렬화되지 않고 구성 변경 전반에 값을 유지할 수 있지만 프로세스 종료 전반에는 값을 유지할 수 없습니다. retain은 상태를 직렬화하지 않으므로 쉽게 직렬화할 수 없는 람다 표현식, 흐름, 비트맵과 같은 대형 객체를 유지할 수 있습니다. 예를 들어 retain을 사용하여 미디어 플레이어 (예: ExoPlayer)를 관리하여 구성 변경으로 인해 미디어 재생이 중단되지 않도록 할 수 있습니다.

@Composable

fun MediaPlayer() {

    val applicationContext = LocalContext.current.applicationContext

    val exoPlayer = retain { ExoPlayer.Builder(applicationContext).apply { ... }.build() }

    ...

}

이 기능의 설계에 영향을 미치고 기여한 AndroidDev 커뮤니티 (특히 Circuit팀)에 감사의 말씀을 전합니다.

Material 1.4

material3 라이브러리의 버전 1.4.0에는 여러 새로운 구성요소와 개선사항이 추가되었습니다.

  • TextField는 이제 텍스트의 상태를 관리하는 더 강력한 방법을 제공하는 TextFieldState 기반 실험용 버전을 제공합니다. 또한 새로운 SecureTextFieldOutlinedSecureTextField 변형이 제공됩니다. 이제 material Text 컴포저블이 autoSize 동작을 지원합니다.
  • 이제 캐러셀 구성요소에서 새로운 HorizontalCenteredHeroCarousel 변형을 제공합니다.
  • TimePicker 이제 선택 도구와 입력 모드 간 전환을 지원합니다.
  • 세로 드래그 핸들을 사용하면 사용자가 적응형 창의 크기 또는 위치를 변경할 수 있습니다.
centered-hero-carousel.webp

가로 가운데 영웅 캐러셀

Material 3 Expressive API는 material3 라이브러리의 알파 출시에서 계속 개발되고 있습니다. 자세한 내용은 이 최근 강연을 참고하세요.

새로운 애니메이션 기능

공유 요소 애니메이션 맞춤설정을 위한 업데이트를 비롯하여 애니메이션 API를 계속 확장하고 있습니다.

동적 공유 요소

기본적으로 sharedElement()sharedBounds() 애니메이션은

타겟 상태에서 일치하는 키가 발견될 때마다 레이아웃 변경사항을 애니메이션 처리하려고 시도합니다. 그러나 탐색 방향 또는 현재 UI 상태와 같은 특정 조건에 따라 이 애니메이션을 동적으로 사용 중지할 수 있습니다.

공유 요소 전환이 발생하는지 여부를 제어하려면 이제 rememberSharedContentState()에 전달된 SharedContentConfig를 맞춤설정할 수 있습니다. isEnabled 속성은 공유 요소가 활성 상태인지 여부를 결정합니다.

SharedTransitionLayout {

        val transition = updateTransition(currentState)

        transition.AnimatedContent { targetState ->

            // Create the configuration that depends on state changing.

            fun animationConfig() : SharedTransitionScope.SharedContentConfig {

                return object : SharedTransitionScope.SharedContentConfig {

                    override val SharedTransitionScope.SharedContentState.isEnabled: Boolean

                        get() =

                            // determine whether to perform a shared element transition

                }

            }

}

자세한 내용은 문서를 참고하세요.

Modifier.skipToLookaheadPosition()

이 출시에는 공유 요소 애니메이션을 실행할 때 컴포저블의 최종 위치를 유지하는 새로운 수정자 Modifier.skipToLookaheadPosition()이 추가되었습니다. 이를 통해 카메라의 점진적 공개와 함께 Androidify 샘플에서 볼 수 있는 '공개' 유형 애니메이션과 같은 전환을 실행할 수 있습니다. 자세한 내용은 여기에서 동영상 도움말을 참고하세요.

공유 요소 전환의 초기 속도

이번 출시에는 공유 요소 전환에 초기 속도 (예: 동작에서)를 전달할 수 있는 새로운 공유 요소 전환 API인 prepareTransitionWithInitialVelocity가 추가되었습니다.

Modifier.fillMaxSize()

    .draggable2D(

        rememberDraggable2DState { offset += it },

        onDragStopped = { velocity ->

            // Set up the initial velocity for the upcoming shared element

            // transition.

            sharedContentStateForDraggableCat

                ?.prepareTransitionWithInitialVelocity(velocity)

            showDetails = false

        },

    )
fling-shared.gif

동작의 초기 속도로 시작되는 공유 요소 전환

베일 전환

EnterTransition 및 ExitTransition은 AnimatedVisibility/AnimatedContent 컴포저블이 표시되거나 사라지는 방식을 정의합니다. 새로운 실험용 베일 옵션을 사용하면 콘텐츠를 베일링하거나 스크림할 색상을 지정할 수 있습니다.예를 들어 콘텐츠 위에 반투명 검은색 레이어를 페이드 인/아웃합니다.

veil_2.gif

베일링된 애니메이션 콘텐츠 - 애니메이션 중에 그리드 콘텐츠 위에 있는 반투명 베일 (또는 스크림)을 확인하세요.

AnimatedContent(

    targetState = page,

    modifier = Modifier.fillMaxSize().weight(1f),

    transitionSpec = {

        if (targetState > initialState) {

            (slideInHorizontally { it } togetherWith

                    slideOutHorizontally { -it / 2 } + veilOut(targetColor = veilColor))

        } else {

            slideInHorizontally { -it / 2 } +

                    unveilIn(initialColor = veilColor) togetherWith slideOutHorizontally { it }

        }

    },

) { targetPage ->

    ...

}

예정 변경사항

Modifier.onFirstVisible 지원 중단

Compose 1.9에는 Modifier.onVisibilityChangedModifier.onFirstVisible이 도입되었습니다. 의견을 검토한 결과 Modifier.onFirstVisible의 계약을 결정론적으로 준수할 수 없는 것으로 나타났습니다. 특히 항목이 처음 표시될 때입니다. 예를 들어 지연 레이아웃은 뷰포트에서 스크롤되는 항목을 삭제한 다음 다시 뷰로 스크롤되면 다시 구성할 수 있습니다. 이 경우 onFirstVisible 콜백은 새로 구성된 항목이므로 다시 실행됩니다. onFirstVisible이 포함된 이전에 방문한 화면으로 다시 이동할 때도 유사한 동작이 발생합니다. 따라서 다음 Compose 출시 (1.11)에서 이 수정자를 지원 중단하고 onVisibilityChanged로 이전하는 것이 좋습니다. 자세한 내용은 문서를 참고하세요.

테스트의 코루틴 디스패치

테스트의 코루틴 디스패치를 변경하여 테스트 불안정성을 개선하고 더 많은 문제를 포착할 계획입니다. 현재 테스트는 프로덕션 동작과 다른 UnconfinedTestDispatcher를 사용합니다. 예를 들어 효과가 대기열에 추가되는 대신 즉시 실행될 수 있습니다. 향후 출시에서는 프로덕션 동작과 일치하도록 기본적으로 StandardTestDispatcher를 사용하는 새로운 API를 도입할 계획입니다. 1.10에서 지금 새로운 동작을 사용해 볼 수 있습니다.

@get:Rule // also createAndroidComposeRule, createEmptyComposeRule

val rule = createComposeRule(effectContext = StandardTestDispatcher())

StandardTestDispatcher를 사용하면 작업이 대기열에 추가되므로 composeTestRule.waitForIdle() 또는 composeTestRule.runOnIdle()과 같은 동기화 메커니즘을 사용해야 합니다. 테스트에서 runTest를 사용하는 경우 동기화를 위해 runTest와 Compose 규칙이 동일한 StandardTestDispatcher 인스턴스를 공유해야 합니다.

// 1. Create a SINGLE dispatcher instance

val testDispatcher = StandardTestDispatcher()



// 2. Pass it to your Compose rule

@get:Rule

val composeRule = createComposeRule(effectContext = testDispatcher)



@Test

// 3. Pass the *SAME INSTANCE* to runTest

fun myTest() = runTest(testDispatcher) {

    composeRule.setContent { /* ... */ }

}

도구

훌륭한 API에는 훌륭한 도구가 필요하며 Android 스튜디오에는 Compose 개발자를 위한 여러 가지 최근 추가사항이 있습니다.

이러한 도구가 실제로 어떻게 작동하는지 보려면 이 최근 데모를 시청하세요.

즐거운 구성

Google은 멋지고 풍부한 UI를 만드는 데 필요한 API와 도구를 제공하기 위해 Jetpack Compose에 계속 투자하고 있습니다. 의견을 보내주셔서 감사합니다. 이러한 변경사항 또는 문제 추적기에서 다음에 보고 싶은 사항에 관한 의견을 공유해 주세요.

계속 읽기