fromm μ±„νŒ… 리뉴얼: UI νŠΉμ„±μ„ κ³ λ €ν•œ Compose μ»΄ν¬λ„ŒνŠΈ 섀계

soojin
  • #Android
  • #Compose
  • #Jetpack
  • #Component

λ“€μ–΄κ°€λ©°

μ•ˆλ…•ν•˜μ„Έμš”, λ…Έλ¨ΈμŠ€ Android νŒ€μ˜ μ–‘μˆ˜μ§„μž…λ‹ˆλ‹€. μ§€λ‚œ 2μ›”, 저희 νŒ€μ˜ μƒμ•„λ‹˜κ»˜μ„œ μ±„νŒ… 리뉴얼: MVI λ„μž… 및 μ„±λŠ₯ κ°œμ„ μ΄λΌλŠ” 포슀트λ₯Ό 톡해 2024λ…„ ν•˜λ°˜κΈ°λΆ€ν„° μ˜¬ν•΄ μ΄ˆκΉŒμ§€ μ§„ν–‰ν•œ μ±„νŒ… μ‹œμŠ€ν…œμ˜ λŒ€κ·œλͺ¨ κ°œμ„  μž‘μ—…μ„ μ†Œκ°œν•΄μ£Όμ…¨μŠ΅λ‹ˆλ‹€.

풀버전이 κΆκΈˆν•˜μ‹  뢄듀은 πŸ‘‰ [fromm μ±„νŒ… 리뉴얼: MVI λ„μž… 및 μ„±λŠ₯ κ°œμ„ ] λ°”λ‘œ κ°€κΈ°

μ €λŠ” 이번 μ±„νŒ… 리뉴얼 μž‘μ—…μ—μ„œ UI λ ˆμ΄μ–΄ μž‘μ—…μ„ 주둜 λ§‘μ•„ μ§„ν–‰ν–ˆμŠ΅λ‹ˆλ‹€. μƒˆλ‘œ μž‘μ„±ν•œ Compose UI μ»΄ν¬λ„ŒνŠΈ μ½”λ“œλ§Œ 해도 5000쀄이 λ„˜μ„ μ •λ„λ‘œ μ±„νŒ… κΈ°λŠ₯은 μƒλ‹Ήνžˆ 큰 λ²”μœ„μ˜ μž‘μ—…μ΄μ—ˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ, μ±„νŒ… 리뉴얼 ν”„λ‘œμ νŠΈμ˜ λͺ©μ μ΄ μœ μ§€λ³΄μˆ˜μ™€ κΈ°λŠ₯ ν™•μž₯을 μš©μ΄ν•˜κ²Œ ν•˜κΈ° μœ„ν•¨μ΄μ—ˆκΈ° λ•Œλ¬Έμ— UI μ»΄ν¬λ„ŒνŠΈ λ˜ν•œ κ·Έ λͺ©μ μ„ μΆ©μ‘±ν•˜κ³ μž ν–ˆμŠ΅λ‹ˆλ‹€. μ˜€λŠ˜μ€ μ•žμ„  κΈ€μ—μ„œ 미처 닀루지 λͺ»ν–ˆλ˜ Jetpack Compose λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ κ³Όμ •μ—μ„œμ˜ 고민듀을 κ³΅μœ ν•΄λ³΄λ € ν•©λ‹ˆλ‹€.

1. Slot API 적극 ν™œμš©ν•˜κΈ° β€” λΆ€ν’ˆ 쑰립식 UI ꡬ성

frommμ—μ„œ μ•„ν‹°μŠ€νŠΈ 뢄듀은 λ‹€μ–‘ν•œ νƒ€μž…μ˜ λ©”μ‹œμ§€λ₯Ό 보낼 수 μžˆμŠ΅λ‹ˆλ‹€.

λ©”μ‹œμ§€ νƒ€μž…λ“€

🀫 μ•„ν‹°μŠ€νŠΈ λΆ„λ“€μ˜ 프라이빗 λ©”μ‹œμ§€λ₯Ό λ³΄ν˜Έν•˜κΈ° μœ„ν•΄ λ³Έ ν¬μŠ€νŠΈμ— μ²¨λΆ€λœ 메세지듀은 개발 ν™˜κ²½ μ•„ν‹°μŠ€νŠΈμ˜ 캑쳐본으둜 λŒ€μ²΄ν•©λ‹ˆλ‹€. 개발 ν™˜κ²½ μ•„ν‹°μŠ€νŠΈκ°€ λˆ„κ΅¬λƒκ³ μš”? λ°”λ‘œ μ ‘λ‹ˆλ‹€ γ…Ž

이미 눈치 채신 뢄듀도 κ³„μ‹œκ² μ§€μš”? λ‹€λ₯Έ νƒ€μž…μ˜ λ©”μ‹œμ§€λ”λΌλ„ λ™μΌν•œ ꡬ쑰λ₯Ό κ°€μ§€λŠ” 뢀뢄이 μžˆμŠ΅λ‹ˆλ‹€. μ €λŠ” λ©”μ‹œμ§€ UI듀을 μ•„λž˜μ™€ 같이 ꡬ쑰화 ν–ˆμŠ΅λ‹ˆλ‹€.

λ©”μ‹œμ§€ νƒ€μž… ꡬ쑰화

κ·Έλž˜μ„œ Jetpack Compose의 Slot API κ°œλ…μ„ 적극 ν™œμš©ν•΄ 곡톡 UI ꡬ쑰λ₯Ό λ§Œλ“€κ³ , 각 νƒ€μž…λ³„λ‘œ 차이 λ‚˜λŠ” λΆ€λΆ„λ§Œ λΌμ›Œ λ„£λŠ” λ°©μ‹μœΌλ‘œ μ‘°λ¦½ν–ˆμŠ΅λ‹ˆλ‹€. μ‹€μ œλ‘œ μ±„νŒ… 리뉴얼 이후 νŠΉμ • λΆ€λΆ„μ˜ UI 변경이 생겼을 λ•Œ, μ΅œμ†Œν•œμ˜ μˆ˜μ •μœΌλ‘œλ„ λ‹€μ–‘ν•œ νƒ€μž…μ— 일괄 μ μš©λ˜μ–΄ μž‘μ—… μ‹œκ°„μ΄ λ‹¨μΆ•λ˜μ—ˆμŠ΅λ‹ˆλ‹€. 그리고 μƒˆλ‘œμš΄ λ©”μ‹œμ§€ νƒ€μž…μ΄ 생겼을 λ•Œμ—λ„, κ·Έμ € λΉˆμΉΈμ— λ“€μ–΄κ°ˆ μ»΄ν¬λ„ŒνŠΈλ§Œ μƒˆλ‘œ μ§œμ„œ μ‘°λ¦½ν•˜κΈ°λ§Œ ν•˜λ©΄ λμ΄μ—ˆμŠ΅λ‹ˆλ‹€.

λ¬Όλ‘  단점도 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. μ»΄ν¬λ„ŒνŠΈλ₯Ό 잘게 λ‚˜λˆ„λ‹€ λ³΄λ‹ˆ μ»΄ν¬λ„ŒνŠΈμ˜ depthκ°€ κΉŠμ–΄μ‘Œκ³ , λ””λ²„κΉ…μ΄λ‚˜ Preview μž‘μ—…μ΄ 쑰금 λ²ˆκ±°λ‘œμ›Œμ§€κΈ°λ„ ν–ˆμŠ΅λ‹ˆλ‹€. μ‹€μ œλ‘œ β€˜μ±„νŒ…λ°© 글씨 크기’ μ˜΅μ…˜μ„ 쀑간 μ»΄ν¬λ„ŒνŠΈμ—λŠ” 내리고, μ‹€μ œ μ‚¬μš©ν•˜λŠ” ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈμ— 내리지 μ•Šμ€ 것을 μ•Œμ•„μ±„μ§€ λͺ»ν•œ μ‹€μˆ˜λ„ ν•œ 번 ν–ˆμŠ΅λ‹ˆλ‹€.

μ‹€μˆ˜

ν•˜μ§€λ§Œ ꡬ쑰적인 이점이 훨씬 크게 느껴쑌기 λ•Œλ¬Έμ— ꡬ쑰λ₯Ό λ°”κΎΈκΈ° λ³΄λ‹€λŠ” 단점을 상쇄할 수 μžˆλŠ” 방법을 κ³ λ―Όν–ˆμŠ΅λ‹ˆλ‹€.

2. CompositionLocal β€” νŠΉμ • λ²”μœ„μ— μ•”μ‹œμ μœΌλ‘œ 데이터 전달

μ±„νŒ…λ°© ν…Œλ§ˆ

fromm μ•±μ—μ„œλŠ” μ±„νŒ…λ°©μ— β€˜ν…Œλ§ˆβ€™λ₯Ό μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν…Œλ§ˆλ₯Ό μ μš©ν•˜λ©΄ μ±„νŒ…λ°© λ°°κ²½, λ©”μ‹œμ§€ λ°°κ²½, λ©”μ‹œμ§€ ν…μŠ€νŠΈ λ“± λ‹€μ–‘ν•œ μ˜μ—­μ˜ 색상이 λ³€κ²½λ©λ‹ˆλ‹€. μ²˜μŒμ—λŠ” ν…Œλ§ˆ 속성듀을 μƒμœ„ μ»΄ν¬μ €λΈ”μ—μ„œ ν•˜μœ„λ‘œ 쀄쀄이 νŒŒλΌλ―Έν„°λ‘œ λ„˜κΈ°κ³  μžˆμ—ˆμŠ΅λ‹ˆλ‹€. μ»΄ν¬λ„ŒνŠΈκ°€ 겹겹이 싸인 ν˜•νƒœμ΄λ‹€λ³΄λ‹ˆ, 직접 μ‚¬μš©ν•˜λŠ” 값이 μ•„λ‹˜μ—λ„ ν•˜μœ„λ‘œ λ„˜κΈ°κΈ° μœ„ν•΄ λ°›λŠ” μ»΄ν¬λ„ŒνŠΈλ„ λ‹€μˆ˜ μ‘΄μž¬ν–ˆμŠ΅λ‹ˆλ‹€. 그리고 ν…Œλ§ˆλ‘œ 인해 νŒŒλΌλ―Έν„°μ˜ κ°œμˆ˜κ°€ λ§Žμ•„μ‘ŒκΈ° λ•Œλ¬Έμ— μ»΄ν¬λ„ŒνŠΈμ˜ 가독성도 λ–¨μ–΄μ‘ŒμŠ΅λ‹ˆλ‹€. μ•„λž˜ 도식도와 같은 ν˜„μƒμ΄ 받은 λ©”μ‹œμ§€/보낸 λ©”μ‹œμ§€ * ν…μŠ€νŠΈ/사진/λ™μ˜μƒ/μŒμ„±λ©”μ‹œμ§€/+@ = μ•½ 10κ°€μ§€κ°€ λ„˜λŠ” μ»΄ν¬λ„ŒνŠΈμ—μ„œ λ°˜λ³΅λ˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

μ»΄ν¬λ„ŒνŠΈ depth

μ—¬κΈ°μ„œ μ„ νƒν•œ 해법이 λ°”λ‘œ CompositionLocalμ΄μ—ˆμŠ΅λ‹ˆλ‹€.

CompositionLocal은 μ•”μ‹œμ μœΌλ‘œ μ»΄ν¬μ§€μ…˜μ„ 톡해 데이터λ₯Ό μ „λ‹¬ν•˜λŠ” λ„κ΅¬μž…λ‹ˆλ‹€. μ•”μ‹œμ μœΌλ‘œ 객체λ₯Ό μ „λ‹¬ν•˜κΈ° λ•Œλ¬Έμ— λ‚¨μš©ν•˜κ²Œ λœλ‹€λ©΄ CompositionLocal은 μ»΄ν¬μ €λΈ”μ˜ λ™μž‘μ„ μΆ”λ‘ ν•˜κΈ° μ–΄λ ΅κ²Œ λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€. μ‹€μ œλ‘œ 곡식 λ¬Έμ„œμ—μ„œλŠ” CompositionLocal μ‚¬μš© μ—¬λΆ€λ₯Ό κ²°μ •ν•˜λŠ” κ°€μ΄λ“œ ν•­λͺ©κΉŒμ§€ μ‘΄μž¬ν•©λ‹ˆλ‹€. μ±„νŒ…λ°© ν…Œλ§ˆ 적용 μ‹œλ‚˜λ¦¬μ˜€κ°€ μ •λ§λ‘œ μ ν•©ν•œμ§€ ν•œλ²ˆ μ κ²€ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

βœ… λ§€κ°œλ³€μˆ˜κ°€ 크둜슀 μ»€νŒ…μ΄κ³  κ΅¬ν˜„μ˜ 쀑간 λ ˆμ΄μ–΄κ°€ κ·Έ 쑴재λ₯Ό μΈμ‹ν•΄μ„œλŠ” μ•ˆ λ˜λŠ” 경우 πŸ‘‰ μ§€κΈˆ 문제 상황과 λ˜‘κ°™μŒ

βœ… μ μ ˆν•œ 기본값이 μžˆμ–΄μ•Ό 함 πŸ‘‰ ν…Œλ§ˆ 색상 λ―Έμ§€μ •μ‹œ, κΈ°λ³Έ 색상을 μ μš©ν•˜λŠ” κ·œμΉ™μ΄ 있음

βœ… 잠재적으둜 일뢀 ν•˜μœ„ μš”μ†Œκ°€ μ•„λ‹Œ λͺ¨λ“  ν•˜μœ„ μš”μ†Œμ—μ„œ μ‚¬μš©ν•  수 μžˆμ„ λ•Œ 적합 πŸ‘‰ μ±„νŒ…λ°© λ‚΄ μ»΄ν¬λ„ŒνŠΈμ˜ 90%에 ν…Œλ§ˆκ°€ 적용

λ”°λΌμ„œ μ €λŠ” CompositionLocal이 ν•΄λ²•μœΌλ‘œ μ ν•©ν•˜λ‹€ νŒλ‹¨ν–ˆμŠ΅λ‹ˆλ‹€.

val LocalChatThemeColors: ProvidableCompositionLocal<ChatThemeColors?> = compositionLocalOf { null }

//...

CompositionLocalProvider(
  LocalChatThemeColors provides state.chatTheme?.toChatThemeColors()
) {
    FanChatRoomContent()
}

μ±„νŒ…λ°© 계측 ꡬ쑰가 μ‹œμž‘λ˜λŠ” Screen μ»΄ν¬λ„ŒνŠΈμ—μ„œ CompositionLocal μΈμŠ€ν„΄μŠ€μ— ν…Œλ§ˆ 색상 값을 κ°€μ§„ 객체λ₯Ό 바인딩 ν–ˆμŠ΅λ‹ˆλ‹€. 이제 쀑간 μ»΄ν¬λ„ŒνŠΈμ—μ„œ νŒŒλΌλ―Έν„°λ₯Ό μ „λ‹¬ν•˜μ§€ μ•Šκ³ , μ‹€μ œλ‘œ ν…Œλ§ˆ 색상 값이 μ μš©λ˜λŠ” κ³³μ—μ„œ μ•”μ‹œμ μœΌλ‘œ 흐λ₯΄λŠ” μ±„νŒ…λ°© ν…Œλ§ˆ 객체λ₯Ό μ·¨λ“ν•˜μ—¬ 색상을 μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

ν•˜μ§€λ§Œ μ μš©ν•˜κ³  λ‚œ ν›„, 또 κ³ λ―Όν•΄μ•Ό ν•  지점을 λ°œκ²¬ν–ˆμŠ΅λ‹ˆλ‹€.

μ²˜μŒμ— 무심코 μ‚¬μš©ν•˜λ‹€λ³΄λ‹ˆ 어디에선 ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈμ—μ„œ 값을 CompositionLocal.current둜 λ°”λ‘œ κΊΌλ‚΄ μ“°κ³  있고, 또 μ–΄λ””μ—μ„œλŠ” μƒμœ„ μ»΄ν¬λ„ŒνŠΈμ—μ„œ ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈλ‘œ νŒŒλΌλ―Έν„°λ‘œ λ„˜κ²¨μ„œ μ‚¬μš©ν•˜κΈ°λ„ ν–ˆμŠ΅λ‹ˆλ‹€. 일관성 없이 μ‚¬μš©ν–ˆλ”λ‹ˆ μ–΄λ””μ—μ„œ 값을 λˆ„λ½ν•΄λ„ μ•Œμ•„μ±„κΈ°λ„ νž˜λ“€λΏλ”λŸ¬, λˆ„λ½μ‹œν‚¨ 뢀뢄을 μ°ΎλŠ” 것도 μΌμ΄μ—ˆμŠ΅λ‹ˆλ‹€. μœ„μ—μ„œ λ§ν•œ depthκ°€ κΉŠμ€ μ»΄ν¬λ„ŒνŠΈμ˜ 디버깅이 νž˜λ“€λ‹€λŠ” 단점이 λ”μš± κ°•ν•΄μ Έ 버린 κ²ƒμž…λ‹ˆλ‹€.

κ·Έλž˜μ„œ κ·œμΉ™μ„ μ •ν–ˆμŠ΅λ‹ˆλ‹€:

  • μ‹€μ œ μ‚¬μš©ν•˜λŠ” κ°€μž₯ ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈμ—μ„œ CompositionLocal.currentλ₯Ό ν˜ΈμΆœν•˜λ˜
  • Preview와 UI Testλ₯Ό μœ„ν•΄ μ™ΈλΆ€μ—μ„œ νŒŒλΌλ―Έν„°λ‘œλ„ 받을 수 있게 μ—΄μ–΄λ‘˜ 것.

@Composable
internal fun ReceivedBubbleBox(
    //...
    color: Color = FrommTheme.colors.surfaceBasic03,
    content: @Composable () -> Unit
) {
    Box(
        modifier = modifier
            .background(color = LocalChatThemeColors.current?.receivedMessageBackground ?: color,),
        content = {
            content()
        }
    )
}

μ΄λ ‡κ²Œ κ·œμΉ™μ„ μ •ν•˜μ—¬ μ μš©ν•˜λ‹ˆ μ‹€μˆ˜κ°€ λ°©μ§€λ˜κ³ , 디버깅도 μš©μ΄ν•΄μ‘ŒμŠ΅λ‹ˆλ‹€. νŠΉμ • 색상이 μ μš©λ˜μ§€ μ•Šμ•˜λ‹€? 그러면 κ·Έ 색상이 μ μš©λ˜μ–΄μ•Ό ν•˜λŠ” μ»΄ν¬λ„ŒνŠΈμ—μ„œ λΉΌλ¨Ήμ€κ²Œ λΆ„λͺ…ν•΄μ‘ŒμœΌλ‹ˆκΉŒμš”. μœ— λ‹¨λ½μ—μ„œ μ–ΈκΈ‰ν•œ μ‹€μˆ˜μΈ β€˜μ±„νŒ…λ°© 글씨 크기’ μ˜΅μ…˜ λ˜ν•œ μ±„νŒ…λ°© ν…Œλ§ˆμ™€ λ˜‘κ°™μ€ 성격을 κ°€μ‘ŒκΈ° λ•Œλ¬Έμ— 같은 방식을 μ±„νƒν•¨μœΌλ‘œμ„œ κ°œμ„ ν–ˆμŠ΅λ‹ˆλ‹€.

3. LazyColumn κ΅¬μ›Œμ‚ΆκΈ° β€” 슀크둀과의 μ „μŸ

reverseLayout

κ²°λ‘ λΆ€ν„° λ§ν•˜μžλ©΄, LazyColumn의 reverseLayout μ˜΅μ…˜μ„ μ£Όμ–΄ 거꾸둜 μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€. β€˜μ±„νŒ…β€™μ΄λΌλŠ” κΈ°λŠ₯의 νŠΉμ§•μ„ 잘 μƒκ°ν•΄λ΄…μ‹œλ‹€. 보톡 μ±„νŒ…μ„ ν• λ•Œ κ°€μž₯ 였래 λ¨Έλ¬΄λŠ” 뢀뢄은 μ–΄λ””μΌκΉŒμš”? λ°”λ‘œ μ΅œμ‹  λ©”μ‹œμ§€κ°€ μžˆλŠ” λΆ€λΆ„μž…λ‹ˆλ‹€. κ·Έλ ‡λ‹€λ©΄ μ΅œμ‹  λ©”μ‹œμ§€λŠ” 어디에 μžˆμ„κΉŒμš”? λ°”λ‘œ 리슀트의 제일 μ•„λž«μͺ½μž…λ‹ˆλ‹€!

μ²˜μŒμ—λŠ” LazyColumn κΈ°λ³Έ μ„€μ •μœΌλ‘œ κ°œλ°œμ„ ν–ˆμŠ΅λ‹ˆλ‹€. λ¦¬μŠ€νŠΈμ— μ˜€λ¦„μ°¨μˆœμœΌλ‘œ λ©”μ‹œμ§€λ₯Ό λ„£κ³ , 리슀트 λ§ˆμ§€λ§‰μ— μ΅œμ‹  λ©”μ‹œμ§€λ₯Ό μΆ”κ°€ν•˜λŠ” ν˜•νƒœμ΄μ§€μš”. κ°œλ°œμ„ ν•˜λ©΄μ„œ μ—¬λŸ¬ λ¬Έμ œμ— λΆ€λ”ͺ히고 μ±„νŒ…μ˜ νŠΉμ„±μ— λŒ€ν•΄ κ³ λ―Όν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€. κ·Έλ•Œ reverseLayout을 μ μš©ν•΄λ³΄λ©΄ μ–΄λ–€κ°€ ν•˜λŠ” 생각이 μŠ€μ³μ§€λ‚˜κ°”μŠ΅λ‹ˆλ‹€. domain λ ˆμ΄μ–΄μ—μ„œλΆ€ν„° λ©”μ‹œμ§€λ₯Ό λ‚΄λ¦Όμ°¨μˆœμœΌλ‘œ κ°€μ Έμ˜¬ 수 μžˆλ„λ‘ λŒ€κ·œλͺ¨ μˆ˜μ •μ„ ν•΄μ•Όλ§Œ ν–ˆμ§€λ§Œ 이점이 더 크닀고 μƒκ°ν•˜μ—¬ ν™• λ’€μ§‘μ–΄ λ²„λ ΈμŠ΅λ‹ˆλ‹€.

κ³ μ£  μ•ˆλ“œλ‘œμ΄λ“œ

"λ°˜μ „μˆ μ‹ reverseLayout"

κ·Έλž˜μ„œ μ–΄λ–€ 이점이 μžˆμ—ˆλƒκ³ μš”?

μž…μž₯ μ‹œ 슀크둀이 μžμ—°μŠ€λŸ½λ‹€

  • μ±„νŒ…λ°© μ§„μž… μ‹œ, μ΅œμ‹  λ©”μ‹œμ§€ 리슀트λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€. μ΄λ•Œ 100개λ₯Ό κ°€μ Έμ˜¨λ‹€κ³  κ°€μ •ν•œλ‹€λ©΄?
  • before
    • μ§„μž…κ³Ό λ™μ‹œμ— κ°€μž₯ μ΅œμ‹  λ©”μ‹œμ§€μΈ 100번째 λ©”μ‹œμ§€μœΌλ‘œ 슀크둀 이동
    • μˆœκ°„μ μœΌλ‘œ 초기 슀크둀인 1번째 λ©”μ‹œμ§€μ—μ„œ 100번째 λ©”μ‹œμ§€λ‘œ 덜컀덩! 슀크둀이 λ³€ν•˜λŠ” λͺ¨μŠ΅μ΄ λˆˆμ— λ³΄μž„
  • after
    • κ°€μž₯ μ΅œμ‹  λ©”μ‹œμ§€λŠ” 1번째 λ©”μ‹œμ§€
    • 슀크둀 ν•˜μ§€ μ•Šμ•„λ„ 이미 μ΅œμ‹  λ©”μ‹œμ§€κ°€ μ΅œν•˜λ‹¨μ— μœ„μΉ˜

μƒˆ λ©”μ‹œμ§€ μˆ˜μ‹  μ‹œ 슀크둀 λΆ€λ‹΄ κ°μ†Œ

  • before
    • μƒˆ λ©”μ‹œμ§€ μˆ˜μ‹  μ‹œ, μ•„λž˜λ‘œ μŠ€ν¬λ‘€μ„ λ°€μ–΄μ•Ό 함
    • μ΅œμ‹  λ©”μ‹œμ§€μ˜ 높이가 λŠ˜μ–΄λ‚˜λŠ” μΌ€μ΄μŠ€κ°€ 있음. μ΄λ•Œ λŠ˜μ–΄λ‚œ 만큼 ν•˜λ‹¨μ— λ©”μ‹œμ§€κ°€ 잘렀 보이기 λ•Œλ¬Έμ— 또 μŠ€ν¬λ‘€μ„ λ°€μ–΄μ•Ό 함
  • after
    • 1번째 λ©”μ‹œμ§€κΉŒμ§€ λ‚΄λ €κ°€ μžˆλŠ” μƒνƒœλΌλ©΄ κ·ΈλŒ€λ‘œ μŠ€ν¬λ‘€μ„ μœ μ§€ν•˜λ©΄ 됨
    • μ΅œμ‹  λ©”μ‹œμ§€μ˜ 높이가 λŠ˜μ–΄λ‚˜λ„ μœ„μͺ½μœΌλ‘œ 밀리기 λ•Œλ¬Έμ— 슀크둀 μ‹ κ²½ μ“Έ ν•„μš”κ°€ 없어짐

μ‚¬μš©μž μ•‘μ…˜ 처리 κ°„κ²°ν™”

  • μ±„νŒ…λ°©μ€ λ©”μ‹œμ§€ 전솑, FAB λ²„νŠΌμœΌλ‘œ 이동 λ“±μ˜ μ•‘μ…˜μœΌλ‘œ μ΅œν•˜λ‹¨ 이동이 자주 μΌμ–΄λ‚˜λŠ” ν™˜κ²½μž…λ‹ˆλ‹€.
  • before
    • μ΅œμ‹  λ©”μ‹œμ§€λ‘œ μ΄λ™μ‹œ, λ¦¬μŠ€νŠΈμ— μžˆλŠ” 총 λ©”μ‹œμ§€ 개수λ₯Ό μ•Œμ•„μ•Ό 이동 κ°€λŠ₯
    • lazyListState.scrollToItem(messages.lastIndex)
  • after
    • μ•„.묻.λ”° 0번째 인덱슀둜 μŠ€ν¬λ‘€μ„ 내리면 그게 μ΅œμ‹  λ©”μ‹œμ§€
    • lazyListState.scrollToItem(0)

슀크둀 μ±…μž„ 뢄리

μ§€κΈˆκΉŒμ§€ μ΅œν•˜λ‹¨μœΌλ‘œ μŠ€ν¬λ‘€ν•˜λŠ” κ²½μš°μ— λŒ€ν•΄ 언급을 ν–ˆμ§€λ§Œ, μ΅œν•˜λ‹¨ 뿐만 μ•„λ‹ˆλΌ νŠΉμ • λ©”μ‹œμ§€λ‘œ μ΄λ™ν•˜λŠ” κ²½μš°λ„ μ‘΄μž¬ν•©λ‹ˆλ‹€.

검색

λŒ€ν‘œμ μΈ μ˜ˆμ‹œλ‘œ β€˜κ²€μƒ‰ κΈ°λŠ₯’이 μžˆμŠ΅λ‹ˆλ‹€. 검색 κ²°κ³Όκ°€ 있으면 λ©”μ‹œμ§€λ₯Ό 화면에 ν‘œμ‹œν•˜κ³ , ν•˜λ‹¨μ˜ λ²„νŠΌμœΌλ‘œ 이전 ν˜Ήμ€ λ‹€μŒ κ²€μƒ‰κ²°κ³Όλ‘œ 이동할 수 μžˆμŠ΅λ‹ˆλ‹€. κΈ°λŠ₯ μžμ²΄λŠ” μ‹¬ν”Œν•΄ λ³΄μ΄μ§€λ§Œ λ‘œμ§μ„ ν’€μ–΄μ“°λ©΄ μƒλ‹Ήνžˆ λ³΅μž‘ν•œ νŽΈμž…λ‹ˆλ‹€.

  1. 검색 결과에 ν•΄λ‹Ήν•˜λŠ” λ©”μ‹œμ§€κ°€ λ©”λͺ¨λ¦¬μ— μžˆλŠ”κ°€?
  • 있으면 κ·Έ λ©”μ‹œμ§€λ‘œ 슀크둀 이동
  1. λ©”λͺ¨λ¦¬μ— μ—†λ‹€λ©΄ db에 μ €μž₯된 λ©”μ‹œμ§€ 쀑 검색어가 ν¬ν•¨λœ λ©”μ‹œμ§€λ₯Ό μ°ΎλŠ”λ‹€.
  2. 2λ²ˆμ—μ„œ 찾은 λ©”μ‹œμ§€ μ•žλ’€λ‘œ λ©”μ‹œμ§€λ“€μ„ κ°€μ Έμ˜¨λ‹€.
  3. 화면에 3λ²ˆμ—μ„œ κ°€μ Έμ˜¨ λ©”μ‹œμ§€λ“€μ„ 뿌리고, 2λ²ˆμ—μ„œ 찾은 λ©”μ‹œμ§€λ‘œ 슀크둀 μ΄λ™ν•œλ‹€.
  4. 더이상 검색결과가 없을 λ•ŒκΉŒμ§€ 이전/λ‹€μŒ λ²„νŠΌμ„ λˆ„λ₯Όλ•Œλ§ˆλ‹€ 1~4번의 과정을 λ°˜λ³΅ν•œλ‹€.

사싀 UI μ—μ„œλŠ” 이 λͺ¨λ“  과정을 μ•Œκ³  μ‹Άμ§€ μ•ŠμŠ΅λ‹ˆλ‹€. β€˜μ–΄λ–€ λ©”μ‹œμ§€λ“€μ„ 화면에 λΏŒλ¦΄μ§€β€™, β€˜μ–΄λ–€ λ©”μ‹œμ§€λ‘œ 슀크둀 μ΄λ™ν• μ§€β€™λ§Œμ΄ κ΄€μ‹¬μ‚¬μž…λ‹ˆλ‹€. μ—¬κΈ°μ„œ 잠깐! μ±„νŒ… 리뉴얼 1편인 μƒμ•„λ‹˜μ˜ 포슀트의 제λͺ©μ—μ„œ μ•Œ 수 μžˆλ“―, 저희 νŒ€μ€ MVI νŒ¨ν„΄μ„ μ μš©ν•˜μ—¬ UI μ—…λ°μ΄νŠΈλ₯Ό κ΄€λ¦¬ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 아직도 읽지 μ•Šμ•˜λ‹€λ©΄?! μ§€κΈˆ λ‹Ήμž₯ πŸ‘‰ [fromm μ±„νŒ… 리뉴얼: MVI λ„μž… 및 μ„±λŠ₯ κ°œμ„ ] λ°”λ‘œ κ°€κΈ°

μ΄λ•Œ μŠ€ν¬λ‘€μ„ State와 Effect 쀑 λ¬΄μ—‡μœΌλ‘œ 관리해야 ν• κΉŒμš”?

  • μ΄ˆκ°„λ‹¨ μ„€λͺ…
    • State: UI의 ν˜„μž¬ λͺ¨μŠ΅μ„ λ‚˜νƒ€λ‚΄λŠ” 데이터
    • Effect: μƒνƒœ λ³€κ²½κ³Ό λ³„λ„λ‘œ μ²˜λ¦¬ν•΄μ•Ό ν•˜λŠ” μΌνšŒμ„± λ™μž‘

βœ… μ €ν¬λŠ” Effect둜 κ΄€λ¦¬ν•˜κΈ°λ‘œ κ²°μ •ν–ˆμŠ΅λ‹ˆλ‹€. β€˜μ–΄λ””λ‘œ μŠ€ν¬λ‘€μ„ μ΄λ™ν•΄λΌβ€™λΌλŠ” μ΄λ²€νŠΈκ°€ λ°œμƒν•˜λ©΄ LazyListState의 슀크둀 이동 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” μΌνšŒμ„± λ™μž‘μœΌλ‘œ κ°„μ£Όν•˜λŠ” κ²λ‹ˆλ‹€. μƒμ•„λ‹˜ 포슀트의 도식도λ₯Ό 빌렀 검색 κΈ°λŠ₯을 ν‘œν˜„ν•œλ‹€λ©΄ μ•„λž˜μ™€ 같이 λ©λ‹ˆλ‹€.

검색 도식도

  • μ±…μž„μ΄ λͺ…ν™•νžˆ λΆ„λ¦¬λ˜μ—ˆμŠ΅λ‹ˆλ‹€. μ–΄λ–€ λ©”μ‹œμ§€λ‘œ 이동해야 ν•˜λŠ”μ§€μ˜ νŒλ‹¨μ€ ViewModelμ—μ„œ μ²˜λ¦¬ν•˜κ³ , μ‹€μ œ UIμ—μ„œμ˜ 슀크둀 λ™μž‘μ€ Composableμ—μ„œ λ‹¨μˆœνžˆ β€˜μ§€μ • 인덱슀둜 μŠ€ν¬λ‘€β€™λ§Œ μˆ˜ν–‰ν•©λ‹ˆλ‹€.
  • 단방ν–₯ 데이터 νλ¦„μœΌλ‘œ UI λ³€ν™”λ₯Ό μ˜ˆμΈ‘ν•˜κΈ° μš©μ΄ν•©λ‹ˆλ‹€.

데이터 μ΄ˆκΈ°ν™”μ™€ scroll 이벀트 타이밍이 μ–΄κΈ‹λ‚  λ•Œ

검색 κ²°κ³Όλ₯Ό 보여쀄 λ•Œ, λ©”μ‹œμ§€ 데이터 μ΄ˆκΈ°ν™”μ™€ λ™μ‹œμ— μŠ€ν¬λ‘€μ„ μ΄λ™ν•˜κΈ° λ•Œλ¬Έμ— μ’…μ’… LazyColumn의 데이터가 μ—…λ°μ΄νŠΈ 되기 전에 슀크둀 λ™μž‘μ΄ μ‹œμž‘λ˜μ–΄ μ—‰λš±ν•œ λ©”μ‹œμ§€λ‘œ 슀크둀이 μ΄λ™λ˜λŠ” ν˜„μƒμ„ κ²ͺμ—ˆμŠ΅λ‹ˆλ‹€. μ΄λŸ΄λ•ŒλŠ” 잘 μ•Œλ €μ§„ LazyListState의 scrollToItem()μ΄λ‚˜ animateScrollToItem() λ©”μ„œλ“œ λŒ€μ‹  requestScrollToItem() λ©”μ„œλ“œλ₯Ό ν™œμš©ν•΄λ³΄μ„Έμš”!

requestScrollToItem()은 μ¦‰μ‹œ μŠ€ν¬λ‘€ν•˜μ§€ μ•Šκ³ , λ‹€μŒ remeasure μ‹œμ μ— 리슀트λ₯Ό νŠΉμ • ν•­λͺ©μœΌλ‘œ μŠ€ν¬λ‘€ν•˜λ„λ‘ μš”μ²­ν•©λ‹ˆλ‹€. 리슀트 데이터가 λ³€κ²½λ˜μ—ˆλ‹€λ©΄, λ³€κ²½λœ 데이터λ₯Ό κΈ°μ€€μœΌλ‘œ index에 ν•΄λ‹Ήν•˜λŠ” λ°μ΄ν„°λ‘œ 슀크둀 λ©λ‹ˆλ‹€. 즉, 데이터 μ—…λ°μ΄νŠΈ β†’ 슀크둀 이동 μˆœμ„œκ°€ 보μž₯λ˜μ–΄ 데이터가 아직 μ€€λΉ„λ˜μ§€ μ•Šμ•˜λŠ”λ° 슀크둀이 λ¨Όμ € λ°œμƒν•˜μ—¬ 싱크가 λ§žμ§€ μ•ŠλŠ” 문제λ₯Ό λ°©μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ§ˆλ¬΄λ¦¬ν•˜λ©°

이번 포슀트둜 닀룬 λ‚΄μš© 외에도 λ§Žμ€ Composeμ—μ„œμ˜ μ‹œν–‰μ°©μ˜€μ™€ 고민듀이 μžˆμ—ˆμ§€λ§Œ, λͺ¨λ‘ λ‹€λ£° μˆ˜λŠ” μ—†μ–΄ UI νŠΉμ„±μ„ κ³ λ €ν•˜μ—¬ μ„€κ³„ν•˜κ³  κ΅¬ν˜„ν•œ λ‚΄μš©μœΌλ‘œ μ€€λΉ„ν•΄ λ³΄μ•˜μŠ΅λ‹ˆλ‹€. μ΄λ ‡κ²Œ 큰 λ²”μœ„μ˜ UIλ₯Ό μ„€κ³„ν•˜λŠ” 것은 μ²˜μŒμ΄μ—ˆλŠ”λ°λ„ λΆˆκ΅¬ν•˜κ³  λ©”μ‹œμ§€ κΈ°λŠ₯을 κ³ λ„ν™”ν•˜κ³  μžˆλŠ” μ§€κΈˆ, 개발 효율이 크게 ν–₯상됨을 μ‹€κ°ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. μ•žμœΌλ‘œ μƒˆλ‘œμš΄ κΈ°λŠ₯을 λ”μš± λΉ λ₯΄κ³  μ•ˆμ •μ μœΌλ‘œ 선보일 수 μžˆλŠ” 기반이 λ§ˆλ ¨λ˜μ—ˆμœΌλ‹ˆ, μ§€μ†μ μœΌλ‘œ λ°œμ „ν•˜λŠ” μ±„νŒ… κ²½ν—˜μ„ κΈ°λŒ€ν•΄μ£Όμ‹œκΈ° λ°”λžλ‹ˆλ‹€!

ν˜„μž¬ fromm AndroidνŒ€μ€ 인재 μ˜μž… μ€‘μž…λ‹ˆλ‹€! 저희와 ν•¨κ»˜ 더 λ‚˜μ€ μ½”λ“œλ₯Ό λ§Œλ“œλŠ” 것에 관심이 μžˆμœΌμ‹œλ‹€λ©΄?! πŸ‘‰ μ±„μš© 곡고 보러 κ°€κΈ°

← λͺ©λ‘μœΌλ‘œ λŒμ•„κ°€κΈ°

Art Changes Life

λ…Έλ¨ΈμŠ€μ™€ ν•¨κ»˜ μ—”ν„°ν…Œν¬ 산업을 ν˜μ‹ ν•΄λ‚˜κ°ˆ 멀버λ₯Ό μ°ΎμŠ΅λ‹ˆλ‹€.

μ±„μš© 쀑인 곡고 보기