Coil은 Kotlin으로 작성되고 코루틴을 기반으로 하는 Android용 이미지 로딩 라이브러리입니다. 공식 문서에 따르면, 이 라이브러리는 Memory Cache, Disk Cache 및 하드웨어 가속 변환을 지원합니다. Coil은 최소 APK 크기(약 150KB)와 Jetpack Compose와의 완벽한 호환성으로 두드러집니다.
핵심 사항
Coil(Coroutine Image Loader)은 완전히 Kotlin으로 작성되고 비동기 작업에 코루틴을 사용하는 Android용 이미지 로딩 라이브러리입니다. 네트워크, 리소스, 파일 시스템 및 Content Provider에서 비트맵 이미지를 로드하기 위한 통합 API를 제공하며, 자동 다중 레벨 캐싱을 지원합니다.
Glide 및 Picasso와 달리 Coil은 콜백 체인 대신 Kotlin Coroutines를 사용하여 코드를 더 선형적이고 예측 가능하게 만듭니다. 모든 로딩 및 디코딩 작업은 Dispatchers.IO 디스패처를 통해 백그라운드 스레드에서 실행되며, 결과는 명시적 전환 없이 메인 스레드에 전달됩니다.
Coil은 변환(Round, Blur, Grayscale), 전환 애니메이션, SVG 및 GIF, 그리고 비표준 디스플레이를 위한 사용자 정의 Target을 지원합니다. Google I/O 2023에 따르면, Coil은 Glide와 함께 공식 Jetpack Compose 튜토리얼에서 권장됩니다.
ImageLoader는 Coil의 주요 구성 요소로, 로딩 요청 실행 및 캐시 관리를 담당합니다. 각 인스턴스는 MemoryCache, DiskCache, BitmapPool 및 코루틴 풀에 대한 참조를 보유합니다. 기본적으로 Coil.imageLoader(context)를 통해 생성된 싱글톤이 사용됩니다.
ImageRequest는 단일 이미지 로딩 요청을 설명하는 객체입니다: 데이터 소스(URL, URI, Int 리소스), 대상 ImageView 또는 Target, 변환, 캐싱 설정 및 플레이스홀더. ImageRequest는 빌더를 통해 구성되며, 유연성과 가독성을 보장합니다.
val request = ImageRequest.Builder(context)
.data("https://example.com/image.jpg")
.crossfade(true)
.size(512, 512)
.transformations(listOf(RoundedCornersTransformation(12f)))
.memoryCachePolicy(CachePolicy.ENABLED)
.diskCachePolicy(CachePolicy.ENABLED)
.target(imageView)
.build()
구성 후 ImageRequest는 enqueue 또는 execute를 통해 ImageLoader에 전달됩니다. enqueue 메서드는 코루틴을 시작하고 Disposable을 반환하여 화면을 떠날 때 로딩을 취소할 수 있습니다. execute 메서드는 Result를 직접 반환하는 suspend 함수입니다.
ImageLoader는 순차적으로 MemoryCache, DiskCache를 확인하고 둘 다 미스인 경우에만 HttpEngine을 통해 네트워크 요청을 실행합니다. 로딩 후 바이트는 대상 크기를 고려하여 Bitmap으로 디코딩되고, 변환이 적용되며, 결과는 두 캐시 모두에 저장되고 Target에 전달됩니다.
Coil은 구성 요소 아키텍처를 기반으로 구축되었으며, 의존성 주입을 통해 모든 부분을 교체할 수 있습니다. 모든 구성 요소는 ImageLoaderFactory에 등록되고 빌더를 통해 ImageLoader 생성자에 전달됩니다.
ImageLoader는 모든 로딩 작업의 진입점입니다. 각 인스턴스에는 코루틴 풀, BitmapPool, MemoryCache, DiskCache 및 인터셉터 목록이 포함됩니다. 기본적으로 하나의 전역 인스턴스가 생성되지만, 단위 테스트를 위해 격리된 캐시가 있는 별도의 인스턴스를 생성할 수 있습니다.
MemoryCache는 LRU(Least Recently Used) 기반의 인메모리 캐시로, 디코딩된 Bitmap 객체를 저장합니다. 기본 최대 크기는 사용 가능한 애플리케이션 메모리의 25%이지만 32MB 미만이어서는 안 됩니다. 캐시 키는 URL + 크기 + 변환으로 구성되어 오래된 이미지 검색을 방지합니다.
DiskCache는 원시 데이터(JPEG, PNG, WebP) 및 디코딩된 메타데이터를 위한 파일 기반 캐시입니다. 애플리케이션의 캐시 디렉토리에 위치하며 제한을 초과할 때 자동 정리를 지원합니다. 디스크 작업은 디렉토리 및 최대 크기 구성을 갖춘 DiskCache.Builder를 통해 수행됩니다.
Coil은 네트워크 요청을 최소화하고 이미지 표시를 가속화하는 다중 레벨 캐싱 전략을 구현합니다. 각 수준은 고유한 목적과 데이터 수명을 가지고 있습니다.
| 수준 | 저장소 유형 | 수명 | 기본 크기 |
|---|---|---|---|
| Memory Cache | RAM의 Bitmap | LRU 제거까지 | 힙의 25%, 32MB부터 |
| Disk Cache | JPEG/WebP 파일 | 제한 초과까지 | 250MB |
| Http Cache | OkHttp 응답 | Cache-Control 헤더에 따름 | HTTP 클라이언트에 따라 다름 |
Memory Cache는 이미 디코딩된 Bitmap에 대한 즉각적인 액세스를 제공합니다. Disk Cache는 첫 번째 로드 후 네트워크 없이(오프라인 우선) 앱이 작동하도록 보장합니다. OkHttp 수준의 Http Cache는 ETag 및 If-Modified-Since를 사용한 조건부 요청을 처리합니다.
캐싱 정책은 CachePolicy를 통해 요청별로 구성되며 ENABLED, READ_ONLY, WRITE_ONLY, DISABLED의 세 가지 값이 있습니다. 예를 들어, 사용자 아바타의 경우 Memory Cache에 READ_ONLY를, Disk Cache에 ENABLED를 설정할 수 있습니다.
Coil은 애플리케이션 아키텍처에 따라 여러 통합 방법을 제공합니다. 작동하는 코드 예제와 함께 세 가지 주요 시나리오를 살펴보겠습니다.
load는 ImageView의 확장 함수로, 한 줄로 이미지를 로드하는 가장 간단한 방법입니다. 이 함수는 URL, URI, Int 리소스 또는 File과 함께 람다 구성기를 통해 모든 선택적 매개변수를 허용합니다.
imageView.load("https://example.com/photo.jpg") {
crossfade(true)
placeholder(R.drawable.placeholder)
error(R.drawable.error)
size(300, 300)
transformations(CircleCropTransformation())
}
load 메서드는 Disposable을 반환하며, onDestroy에서 또는 View 재사용 시 취소할 수 있습니다. 이는 빠른 목록 스크롤 중 메모리 누수 및 불필요한 네트워크 요청을 방지합니다.
AsyncImage는 선언형 UI에서 이미지를 로드하기 위한 컴포저블 함수입니다. 모든 데이터 소스와 플레이스홀더, 오류, 성공의 세 가지 선택적 상태 매개변수를 허용합니다.
@Composable
fun NetworkImage(url: String) {
AsyncImage(
model = url,
contentDescription = "네트워크 이미지",
placeholder = ColorPainter(Color.Gray),
error = ColorPainter(Color.Red)
)
}
SubcomposeAsyncImage는 콘텐츠 슬롯을 통해 로딩 중 표시를 사용자 정의할 수 있는 더 유연한 버전입니다. 스켈레톤(shimmer) 및 진행 표시줄에 유용합니다.
ImageView 또는 AsyncImage가 적합하지 않은 경우 Bitmap을 허용하는 단일 onSuccess 메서드가 있는 Target을 구현할 수 있습니다. 이는 Notification, RemoteViews 또는 OpenGL 텍스처에 로드하는 데 사용됩니다.
val target = object : BitmapTarget() {
override fun onSuccess(result: Bitmap) {
notificationRemoteView.setImageViewBitmap(R.id.icon, result)
}
}
imageLoader.enqueue(
ImageRequest.Builder(context)
.data(url)
.target(target)
.build()
)
이미지 로딩 라이브러리의 선택은 프로젝트 요구 사항에 따라 다릅니다. Coil은 Glide 및 Picasso와 경쟁하며, 각각 강점이 있습니다. 주요 특성 비교가 표에 나와 있습니다.
| 특성 | Coil | Glide | Picasso |
|---|---|---|---|
| 언어 | Kotlin(100%) | Java + Kotlin | Java |
| APK 크기 | ~150KB | ~500KB | ~120KB |
| 코루틴 | 내장 | 없음(콜백) | 없음(콜백) |
| Jetpack Compose | 기본 지원 | accompanist를 통해 | 타사 |
| GIF/WebP | 예(내장) | 예(내장) | 아니요 |
| Google 권장 | 예(I/O 2023) | 예 | 아니요 |
Kotlin 및 Jetpack Compose의 새로운 프로젝트의 경우, Coil은 추가 코루틴 종속성이 없고 크기가 최소이므로 자연스러운 선택이 됩니다. Glide는 애니메이션 및 비디오 미리보기가 있는 복잡한 시나리오에서 여전히 선호됩니다. Picasso는 기능 면에서 둘 다 뒤떨어지지만 단순성에서 우수합니다.
Android 프로젝트에 Coil을 추가하는 것은 Gradle 종속성을 통해 이루어집니다. 추가 후 라이브러리는 ContentProvider를 통해 자동으로 ImageLoader를 등록하므로 Application에서 수동 초기화가 필요하지 않습니다. 사용자 정의가 필요한 경우 빌더를 통해 사용자 정의 ImageLoader가 생성됩니다.
// build.gradle.kts (app module)
dependencies {
implementation("io.coil-kt:coil:2.6.0")
// Jetpack Compose의 경우 추가:
implementation("io.coil-kt:coil-compose:2.6.0")
// SVG 지원의 경우:
implementation("io.coil-kt:coil-svg:2.6.0")
// GIF 지원의 경우:
implementation("io.coil-kt:coil-gif:2.6.0")
}
ImageLoader를 사용자 정의하려면 ImageLoaderFactory가 사용됩니다 — Application.onCreate에서 생성되는 싱글톤입니다. 팩토리에서 캐시 제한, HTTP 클라이언트, 사용자 정의 디코더 및 로깅을 구성할 수 있습니다. 기본적으로 Coil은 준비된 연결 풀과 함께 OkHttp를 사용합니다.
class App : Application(), ImageLoaderFactory {
override fun newImageLoader(): ImageLoader {
return ImageLoader.Builder(this)
.memoryCache {
MemoryCache.Builder()
.maxSizePercent(0.25)
.build()
}
.diskCache {
DiskCache.Builder()
.directory(cacheDir.resolve("coil_cache"))
.maxSizeBytes(512 * 1024 * 1024)
.build()
}
.build()
}
}
자주 묻는 질문
Coil은 Kotlin으로 코루틴을 사용하여 작성된 Android용 이미지 로딩 라이브러리입니다. 네트워크, 리소스 또는 파일 시스템에서 비트맵 이미지를 비동기적으로 로드, 캐싱 및 표시하는 데 사용됩니다.
Coil은 100% Kotlin으로 작성되었으며 Glide의 콜백 메커니즘 대신 코루틴을 사용합니다. Coil은 APK 크기가 더 작고(~150KB 대 ~500KB) AsyncImage를 통한 Jetpack Compose 기본 지원을 제공합니다.
build.gradle.kts에 종속성 io.coil-kt:coil:2.6.0을 추가하세요. Jetpack Compose의 경우 io.coil-kt:coil-compose:2.6.0도 추가하세요. 라이브러리는 ContentProvider를 통해 자동으로 ImageLoader를 등록합니다.
Coil은 JPEG, PNG, WebP, BMP, SVG(coil-svg 모듈을 통해) 및 GIF(coil-gif 모듈을 통해)를 지원합니다. AVIF 및 HEIF 형식은 Android 10+ 기기에서 사용자 정의 디코더를 통해 지원됩니다.
캐시는 ImageLoader.Builder를 통해 구성됩니다: memoryCache는 힙의 백분율, diskCache는 경로와 바이트 제한을 지정합니다. 캐시 정책(ENABLED, DISABLED, READ_ONLY)은 CachePolicy를 통해 요청별로 구성됩니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.