Android

페스타고 상세 페이지 이동 애니메이션 변경 및 UX 개선

베르_최성훈 2024. 5. 12. 00:47

페스타고가 출시되었습니다!

어느 대학 축제에 어떤 아티스트가 오는지,

어떤 아티스트가 어느 대학 축제에 가는지,

궁금하다면...지금 바로 페스타고!

 

[Android]

https://play.google.com/store/apps/details?id=com.festago.festago

 

페스타고 - 대학 축제, 가수, 공연, 아이돌 - Google Play 앱

전국 대학 축제 검색 플랫폼 페스타고, 대학 축제를 더욱 즐겁게!

play.google.com

 

[iOS]

24.05.15 coming soon..


 

오늘은 페스타고 개발 중 개발 서버와 연동하고 생긴 문제를 어떻게 개선하였는지 전달하고자 합니다.

 

 

평화롭게 개발하던 중.. FakeRepository 에서 DefaultRepository 로 의존성을 변경해주었는데..

 

 

 

애니메이션이 부자연스럽고 버벅이는게 육안으로 보였다. 그래서 애니메이션을 커스텀하여 만들어보았다.

 

1. 애니메이션 수정

 

아이디어는 에브리타임, 네이버 웹툰 등을 참고했다.

 

A 화면 -> B 화면 이동 : A 화면은 작아지면서 사라지고 B 화면은 오른쪽에서 튀어나오는 애니메이션

뒤로가기 (B -> A) : B 화면은 오른쪽으로 사라지고 A 화면 커지면서 나타난다.

<!-- fade in -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/decelerate_interpolator">

    <!-- 서서히 나타남 -->
    <alpha
        android:duration="@integer/nav_Anim_time"
        android:fromAlpha="0.5"
        android:toAlpha="1" />

	<!-- 크기 증가 -->
    <scale
        android:duration="@integer/nav_Anim_time"
        android:fromXScale="98%"
        android:fromYScale="98%"
        android:toXScale="100%"
        android:toYScale="100%" />

	<!-- 위치 이동 -->
    <translate
        android:duration="@integer/nav_Anim_time"
        android:fromXDelta="2%"
        android:fromYDelta="2%"
        android:toXDelta="0%"
        android:toYDelta="0%" />
</set>

<!-- fade out -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/decelerate_interpolator">
    
    <!-- 서서히 사라짐 -->
    <alpha
        android:duration="@integer/nav_Anim_time"
        android:fromAlpha="1"
        android:toAlpha="0.5" />
        
	<!-- 크기 감소 -->
    <scale
        android:duration="@integer/nav_Anim_time"
        android:fromXScale="100%"
        android:fromYScale="100%"
        android:toXScale="98%"
        android:toYScale="98%" />

	<!-- 위치 이동 -->
    <translate
        android:duration="@integer/nav_Anim_time"
        android:fromXDelta="0%"
        android:fromYDelta="0%"
        android:toXDelta="2%"
        android:toYDelta="2%" />
</set>

<!-- slide in -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/decelerate_interpolator">
	<!-- 오른쪽에서 왼쪽으로 -->
    <translate
        android:duration="@integer/nav_Anim_time"
        android:fromXDelta="100%"
        android:toXDelta="0%" />
</set>

<!-- slide out -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/decelerate_interpolator">
    <!-- 왼쪽에서 오른쪽으로 -->
    <translate
        android:duration="@integer/nav_Anim_time"
        android:fromXDelta="0%"
        android:toXDelta="100%" />
</set>

 

결과물

영상이 남아있지 않아서 그때 코드로 재연하였다.

 

 

애니메이션은 좀 자연스러워 졌으나 매우 버벅이는 것을 볼 수 있다.

 

2.  버벅임 해결

 

그 이유를 다음과 같이 생각했다.

 

1. 안드로이드에서 UI 를 그리는 스레드는 메인 스레드 하나이다. 

2. 상세 화면으로 이동하면서 해당 화면에 필요한 정보를 서버 요청 보낼 것이다.

3. 서버로부터 응답을 받으면 응답 받은 것들이 화면에 그려진다.

4. 애니메이션 중간에 서버로부터 응답을 받으면 화면을 그리면서 작업량이 많아져 버벅이게 된다.

 

Android Studio 의 Network Inspection 을 보면 서버 응답시간의 평균이 약 0.05초로 굉장히 빨랐다. 그래서 생각했다.

애니메이션이 끝나고 난 뒤에 요청을 보내도 사용자가 크게 불편함을 느끼지 못할 것이다.

 

class DefaultFestivalRepository @Inject constructor(
    private val festivalRetrofitService: FestivalRetrofitService,
) : FestivalRepository {

    // ...

    // 서버 요청 시 delay 시간을 받는다
    override suspend fun loadFestivalDetail(
    	id: Long, 
        delayTimeMillis: Long
    ): Result<FestivalDetail> {
        
        delay(delayTimeMillis)
        // 서버 요청 및 반환
    }
}

 

Repository 에서 API 요청이 애니메이션 실행 시간 (300 millis) 만큼 delay 후 실행되도록 하였다.

 

참고: 여기서 delay 는 kotlin coroutine 의 delay 이다. 이 delay 는 suspendCancellableCoroutine 을 반환하는데 스레드가 delay 시간만큼 다른 작업을 수행할 수 있도록 하기 때문에 멈춰버리는 thread sleep 과 다르다.

 

 

결과물

 

더 이상 버벅이지 않고 애니메이션이 훨씬 부드러워졌다. 하지만...

 

이렇게 하니깐 또 마음에 들지 않는 부분이 생겼다.

 

1. 데이터가 Default 값에서 Actual 값으로 업데이트 되면서 번쩍이는 것

2. 사용자가 로딩이 끝날 때까지 Default 값을 봐야한다는 것

 

이정도면 괜찮지 0.3 초인데.. 라고 대수롭지 않을 수 있지만 내가 사용자라면 불편할 것 같았다.

 

 

3.  정보 미리보기

잘 생각해보니 목록 화면에서 이미지, 이름 등은 미리 알 수 있었다. 이를 미리 전달해 사용자의 불편을 줄일 수 있지 않을까?

 

객체를 넘기기 위해 직렬화 라이브러리로 Parcelable 을 사용했다.

 

@Parcelize
data class FestivalDetailArgs(val id: Long, val name: String, val posterImageUrl: String) :
    Parcelable

 

필자는 Serializable 도 있지만 안드로이드 의존성을 명확히 가지는 곳에서는 기본 함수를 제공하는 Parcelable 사용을 선호한다.

 

어쨌든 이렇게 id, 이름, 이미지를 같이 보내 화면에 미리 값을 넣어주고 애니메이션이 끝나면 서버 요청을 보낸다.

 

 

1. 이전 화면에서 정의한 Parcelable 객체와 함께 상세 페이지로 navigate 한다.

 

<argument
       android:name="festival"
       app:argType="com.festago.festago.presentation.ui.festivaldetail.FestivalDetailArgs" />

 

findNavController().navigate(
	FestivalListFragmentDirections.actionFestivalListFragmentToFestivalDetailFragment(
		with(event.festival) { FestivalDetailArgs(id, name, imageUrl) },
	),
)

 

 

2. 이전 화면에서 넘어온 이름, 이미지 url 로 초기화

 

fun initView() {
	binding.ivFestivalPoster.setImage(args.festival.posterImageUrl)
        binding.ivFestivalBackground.setImage(args.festival.posterImageUrl)
        binding.tvFestivalName.text = args.festival.name
 }

 

3. 애니메이션 시간이 지나면 서버 요청을 보냄 (300ms)

 

// 앞과 동일

 

4. 결과가 오면 화면을 업데이트

 

결과물

 

현재 페스타고 프로덕션 화면이다.

 

 

 

서버 응답을 받기 전에도 이전 화면에서 넘어온 값들로 미리 정보를 볼 수 있으며 불편함이 줄어들었다.

 

아직 조금 아쉬운 것은 한 번 요청하면 Room DB 를 사용, 로컬에 onDisk 로 캐싱하고 서버 요청 전에 모든 정보를 미리볼 수 있게 하고 싶은데... 이 부분은 출시 날짜를 맞추느라 시간상 구현하지 못하였다. 추후 구현하면 좋을 것 같다!