Programming/Android

[Android/Kotlin] Image URL을 Bitmap으로 변환해서 보여주는 방법

코딩뽀시래기 2024. 6. 28. 21:20
728x90

라이브러리를 사용하지 않고 API에서 URL로 받은 이미지를 화면에 보여주려고 시도하다가 결국 라이브러리를 사용하게 된 사건을 기록하려고 한다,,,

 

처음에는 라이브러리를 사용하지 않기 위해 BitmapFactory로 이미지를 로드하는 코드를 구현했다.

 

[ util module > ImageLoader ]

import android.graphics.Bitmap
import android.graphics.BitmapFactory

object ImageLoader {
    fun byteArrayToBitmap(byteArray: ByteArray): Bitmap {
        return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
    }
}

 

[ domain module > ImageRepository ]

interface ImageRepository {
    suspend fun loadImage(imageUrl: String): ByteArray?
}

 

[ data module > ImageRepositoryImpl ]

class ImageRepositoryImpl @Inject constructor() : ImageRepository {
    override suspend fun loadImage(imageUrl: String): ByteArray? {
        return try {
            val url = URL(imageUrl)
            val stream = url.openStream()
            stream.readBytes()
        } catch (e: MalformedURLException) {
            //Log.d("LoadImage Error", "MalformedURLException occurred: ${e.message}")
            e.printStackTrace()
            null
        } catch (e: IOException) {
            //Log.d("LoadImage Error", "IOException occurred: ${e.message}")
            e.printStackTrace()
            null
        } catch (e: Exception) {
            //Log.d("LoadImage Error", "Unexpected Exception occurred: ${e.message}")
            e.printStackTrace()
            null
        }
    }
}

 

[ domain module > LoadImageUseCase ]

class LoadImageUseCase @Inject constructor(
    private val repository: ImageRepository
){

    suspend operator fun invoke(imageUrl: String):  ByteArray? {
        return repository.loadImage(imageUrl)
    }
}

 

[ feature module > HomeViewModel ]

...
    
    fun loadImages(imageUrls: List<HomeBannerItemVO>) {
        viewModelScope.launch {
            val bitmaps = imageUrls.map { homeBannerVO ->
                val byteArray = withContext(Dispatchers.IO) {
                    loadImageUseCase(homeBannerVO.imageUrl)
                }
                byteArray?.let { ImageLoader.byteArrayToBitmap(it) }
            }.toMutableList()
            _bannerImageUiState.value = bitmaps
        }
    }
    
...

 

 

domain module에서는 Bitmap 자료형을 쓰면 안 되기 때문에, ByteArray로 변환했다가 Bitmap으로 변환하는 과정을 거쳤다.

 

이미지 클릭 이벤트 처리 문제

Bitmap으로 변환한 데이터를 viewpager로 넘겨줬는데, 이미지를 클릭하면 해당 아이템의 id에 따라 다음 화면에 보여줄 데이터를 불러와야 했다...

 

data class HomeBannerItemVO(
    val id: Int = -1,
    val imageUrl: String = ""
)

 

그러나 VO data 형식은 위와 같았고... Bitmap과 id를 함께 저장하고 있는 데이터 클래스는 없었다. VO에 Bitmap을 추가하려니 domain 모듈이라서 안 되고, ByteArray로 바꾼다고 하면 같은 문제가 생길 것 같았다. 그래서 차라리 새로운 데이터 클래스를 만들어볼까 생각하다가 MVVM, 클린 아키텍처 패턴을 지키면서 구현하려면 어떤 모듈에 데이터 클래스를 추가해야 할지 모르겠어서 고민이 되었다.

 

계속 고민하다가 결국 이미지 로더 라이브러리를 사용하기로...

 

이미지 로더 라이브러리는 Glide, coil 등 여러가지가 있는데 coil을 추천하는 포스팅을 보고 coil을 사용하게 되었다.

 

coil을 사용하면 위와 같은 과정 없이 그냥 viewpager adaper에서 아래처럼 해주면 끝

binding.imageHomeBanner.load(item.imageUrl)

 

라이브러리 없이 구현하고 싶었는데, 좀 더 고민을 해봐야겠다. 그리고 coil보다 glide를 사용하는 사람이 더 많은 것 같은데 둘을 비교해보는 시간도 가져야 할 것 같다.

728x90