Coil 이미지 로드가 안될때, 디버깅 하는 방법

이지훈·2024년 2월 27일
0
post-thumbnail

서두

coil 을 사용할 경우, 분명 잘못한게 없는게 이미지가 화면에 로드가 되지 않는 경우가 존재한다. 이때 어떻게 문제의 원인을 찾고, 해결할 수 있는지 알아보도록 하겠다.

TL;DR

Coil 에서 제공하는 AsyncImage 함수 내에 onState 블럭을 통해 이미지의 상태(state) 정보를 파악하고 실패의 원인을 파악할 수 있다.

문제 상황 발생

문제 상황은 다음과 같았다.

@Composable
internal fun MyPageContentUser(
    userInfo: UserInfoEntity,
    albumImgCount: Int,
) {
    if (userInfo.profileImageUrl.isEmpty()) {
        Image(
            painter = painterResource(R.drawable.ic_profile),
            contentDescription = "profile image",
            contentScale = ContentScale.Fit,
            alignment = Alignment.Center,
            modifier = Modifier.size(100.dp),
        )
    } else {
        NetworkImage(
            imageUrl = userInfo.profileImageUrl,
            contentDescription = "profile Image",
            contentScale = ContentScale.Crop,
            modifier = Modifier
                .size(100.dp)
                .clip(CircleShape),
        )
    }
    Spacer(modifier = Modifier.height(20.dp))
    Text(
        text = userInfo.nickname,
        style = Title2,
        color = MaterialTheme.colorScheme.onBackground,
    )
@Composable
fun NetworkImage(
    imageUrl: String,
    contentDescription: String,
    modifier: Modifier = Modifier,
    contentScale: ContentScale = ContentScale.Crop,
) {
    val context = LocalContext.current

    if (LocalInspectionMode.current) {
        Icon(
            imageVector = Icons.Outlined.Person,
            contentDescription = "Network Image Icon",
            modifier = Modifier
                .width(186.dp)
                .aspectRatio(1f),
        )
    } else {
        AsyncImage(
            model = ImageRequest.Builder(context)
                .data(imageUrl)
                .crossfade(true)
                .build(),
            contentDescription = contentDescription,
            contentScale = contentScale,
            modifier = modifier,
        )
    }
}

마이페이지 화면 내에서 유저의 프로필 정보를 보여주는 컴포저블 함수를 작성하고, 서버에서 가져온 프로필 이미지를 로드하기 위해 coil 의 AsyncImage 컴포저블 함수를 사용하였다.

결과)

...?
내가 알고 있는 기존의 지식으로는 코드에 문제는 없어보였다. 어째서 이미지가 로드가 되지 않는 것인가...

문제 해결

문제의 원인을 찾기 위해, NetworkImage 컴포저블에 다음과 같은 코드를 추가해주었다.

        AsyncImage(
            model = ImageRequest.Builder(context)
                .data(imageUrl)
                .crossfade(true)
                .build(),
            contentDescription = contentDescription,
            contentScale = contentScale,
            modifier = modifier,
            onState = { state ->
                when (state) {
                    is AsyncImagePainter.State.Success -> {
                        Timber.d("Image Load Success")
                    }

                    is AsyncImagePainter.State.Error -> {
                        Timber.d("${state.result.throwable.message}")
                    }

                    else -> {}
                }
            },
        )

AsyncImagePainter 클래스내에는 다음과 같은 State sealed class 가 존재하는데,

    sealed class State {

        /** The current painter being drawn by [AsyncImagePainter]. */
        abstract val painter: Painter?

        /** The request has not been started. */
        data object Empty : State() {
            override val painter: Painter? get() = null
        }

        /** The request is in-progress. */
        data class Loading(
            override val painter: Painter?,
        ) : State()

        /** The request was successful. */
        data class Success(
            override val painter: Painter,
            val result: SuccessResult,
        ) : State()

        /** The request failed due to [ErrorResult.throwable]. */
        data class Error(
            override val painter: Painter?,
            val result: ErrorResult,
        ) : State()
    }

마치 Paging3 라이브러리의 LoadState 처럼, 현재 상태, 즉 이미지가 로드되는 동안의 상태를 확인할 수 있는 클래스이다.

초기에 아직 이미지 로드가 시작되기전엔 Empty ,
Loading 중에는 Loading ,
이미지가 성공적으로 로드된 경우엔 Success,
이미지 로드를 실패한 경우엔 Error 이다.

이 중에 내가 관심있는 케이스는 이미지 로드가 성공하였는지, 실패하였는지 이기 때문에 이 두가지 케이스만 코드를 작성하였고, 위의 상황의 경우 이미지 로드를 실패한 케이스이기 때문에 해당 라인에 중단점을 찍어 그 이유를 확인해보도록 하겠다.

결과)

다음과 같이 현재 상태(state)는 Error 이고, ErrorResult 클래스의 파라미터인 throwable 을 통해 무엇 때문에 이미지 로드를 실패하였는지 확인할 수 있었다.

사실 코드를 확인해보면 알겠지만, 로그를 통해서도 원인을 확인할 수 있다. 디버깅이 귀찮은 사람의 경우 로그를 통해 원인을 파악하도록 하자.

문제가 프로필 이미지 url이 http 로 내려오기 때문에 이를 로드할 수 없던 것이기에, app 모듈의 AndroidManifest 에 android:usesCleartxtTraffic="true" 를 추가하여, 문제를 해결할 수 있었다.

Coil 에서는 이처럼 개발자가 개발을 할때, 문제의 원인을 쉽게 파악할 수 있는 방법을 제공한다. 이 글을 공유함으로써, 보다 많은 사람들이 빠른 시간 내에 문제의 원인을 찾아 해결하길 바래본다.

profile
실력은 고통의 총합이다. Android Developer

0개의 댓글