자리표시자와 오류 상태
로딩과 실패 상황을 자연스럽게 처리합니다.
자리표시자와 오류 상태은(는) CoddyKit의 무료 Android Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Android Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Android Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
로딩은 즉시 끝나지 않습니다
네트워크 이미지를 표시하는 데는 시간이 걸립니다. 그동안 처리하지 않으면 UI에는 아무것도 표시되지 않습니다. 또한 URL이 잘못되었거나 서버가 중단되었거나 사용자가 오프라인 상태여서 이미지가 끝내 도착하지 않을 수도 있습니다.
완성도 높은 앱은 빈 공간을 남기거나 앱을 중단하는 대신 로딩 중에는 플레이스홀더를 표시하고, 문제가 발생하면 대체 이미지를 표시합니다. Coil을 사용하면 두 가지 모두 쉽게 구현할 수 있습니다.
이 레슨에서는 플레이스홀더, 오류 이미지, 상태별 사용자 지정 UI를 추가합니다.
간단한 플레이스홀더
플레이스홀더를 추가하는 가장 빠른 방법은 painterResource를 사용해 placeholder 매개변수로 드로어블을 AsyncImage에 전달하는 것입니다. 실제 이미지 로딩이 끝날 때까지 Coil이 이 드로어블을 표시합니다.
회색 상자나 로고 실루엣처럼 중립적이고 가벼운 드로어블을 사용합니다.
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun ImageWithPlaceholder(url: String) {
AsyncImage(
model = url,
contentDescription = null,
placeholder = painterResource(R.drawable.placeholder_gray)
)
}오류 이미지 추가하기
error 매개변수는 요청이 실패했을 때 대체 드로어블을 표시합니다. fallback도 있는데, 이는 model이 null인 경우(로드할 데이터가 전혀 없는 경우)에만 사용됩니다.
플레이스홀더, 오류, 대체 이미지 세 가지를 모두 설정하면 거의 코드 없이 모든 시각적 상태를 처리할 수 있습니다.
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun RobustImage(url: String?) {
AsyncImage(
model = url,
contentDescription = null,
placeholder = painterResource(R.drawable.placeholder_gray),
error = painterResource(R.drawable.image_broken),
fallback = painterResource(R.drawable.no_image)
)
}ImageRequest를 통한 플레이스홀더
이러한 상태를 ImageRequest 자체에 설정할 수도 있습니다. 요청을 한 번 만들어 재사용하거나 다른 요청 옵션을 동시에 설정해야 할 때 유용합니다.
요청 수준 함수에는 드로어블 리소스 ID를 전달합니다.
import android.content.Context
import coil3.request.ImageRequest
import coil3.request.crossfade
import coil3.request.error
import coil3.request.placeholder
fun buildRequest(context: Context, url: String) =
ImageRequest.Builder(context)
.data(url)
.crossfade(true)
.placeholder(R.drawable.placeholder_gray)
.error(R.drawable.image_broken)
.build()SubcomposeAsyncImage으로 사용자 지정 UI 만들기
드로어블도 괜찮지만, 상태별로 스피너, 시머 또는 텍스트를 표시하고 싶을 때가 있습니다. SubcomposeAsyncImage을 사용하면 loading, error, success에 실제 컴포저블을 제공할 수 있습니다.
더 유연하지만 약간 더 무거우므로 긴 목록에서는 드로어블 플레이스홀더를 우선 사용하고, 대표 이미지에는 서브컴포지션을 사용하는 것이 좋습니다.
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import coil3.compose.SubcomposeAsyncImage
@Composable
fun HeroImage(url: String) {
SubcomposeAsyncImage(
model = url,
contentDescription = "Hero",
loading = { CircularProgressIndicator() },
error = { Text("Could not load image") }
)
}상태 직접 읽기
완전히 제어하려면 rememberAsyncImagePainter API를 사용하고 state를 검사합니다. 이 상태는 Loading, Success, Error, Empty 사례를 포함하는 봉인된 타입입니다.
이를 통해 발생한 상황에 따라 자체 레이아웃, 애니메이션 또는 분석을 실행할 수 있습니다.
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import coil3.compose.AsyncImagePainter
import coil3.compose.rememberAsyncImagePainter
@Composable
fun StateAwareImage(url: String) {
val painter = rememberAsyncImagePainter(model = url)
val state = painter.state.collectAsState().value
when (state) {
is AsyncImagePainter.State.Loading -> { /* show spinner */ }
is AsyncImagePainter.State.Error -> { /* show error UI */ }
else -> Image(painter = painter, contentDescription = null)
}
}시머 플레이스홀더 표시하기
널리 사용되는 패턴으로 시머가 있습니다. 시머는 콘텐츠가 곧 나타날 것임을 암시하는 애니메이션 회색 그라데이션입니다. 로딩 상태 뒤에 단색 상자를 배치하면 간단한 버전을 만들 수 있습니다.
여기서는 로딩 중에 색조가 적용된 상자를 표시하도록 SubcomposeAsyncImage을 사용합니다. 애니메이션 효과가 필요하면 상자를 시머 라이브러리로 바꾸면 됩니다.
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import coil3.compose.SubcomposeAsyncImage
@Composable
fun ShimmerImage(url: String) {
SubcomposeAsyncImage(
model = url,
contentDescription = null,
loading = {
androidx.compose.foundation.layout.Box(
Modifier.fillMaxSize()
.background(MaterialTheme.colorScheme.surfaceVariant)
)
}
)
}null 또는 빈 모델 처리하기
예를 들어 프로필 사진이 없는 사용자처럼 아직 URL이 없다면 어떻게 해야 할까요? null을 모델로 전달하면 fallback 드로어블이 실행됩니다(fallback이 설정되지 않은 경우에는 error가 실행됩니다).
이렇게 하면 자체 코드에서 null을 특별히 처리할 필요가 없습니다. Coil이 기본 아바타를 표시하도록 맡기면 됩니다.
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun ProfilePicture(photoUrl: String?) {
// If photoUrl is null, Coil shows the fallback default avatar.
AsyncImage(
model = photoUrl,
contentDescription = "Profile picture",
fallback = painterResource(R.drawable.default_avatar),
error = painterResource(R.drawable.default_avatar)
)
}Listener로 오류 감지하기
로드에 실패했을 때 기록하거나 재시도하는 등 코드에서 반응해야 할 때가 있습니다. ImageRequest.Builder는 성공 및 오류 콜백이 포함된 listener를 받을 수 있습니다.
이 콜백은 메인 스레드에서 실행되므로 가볍게 유지합니다.
import android.content.Context
import android.util.Log
import coil3.request.ImageRequest
fun loggedRequest(context: Context, url: String) =
ImageRequest.Builder(context)
.data(url)
.listener(
onError = { _, result ->
Log.e("Coil", "Load failed", result.throwable)
},
onSuccess = { _, _ ->
Log.d("Coil", "Loaded $url")
}
)
.build()색조 및 색상 필터
플레이스홀더와 오류 드로어블은 테마에 맞게 색조를 적용하면 더 보기 좋은 경우가 많습니다. ColorFilter를 AsyncImage에 적용할 수 있으며, 밝은 모드와 어두운 모드에 맞게 바뀌어야 하는 단색 아이콘 스타일의 대체 이미지에 특히 유용합니다.
필터는 원하는 플레이스홀더 또는 오류 표시 상태에만 적용하고, 실제 사진에는 일반적으로 적용하지 않습니다.
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun TintedFallback(url: String?) {
AsyncImage(
model = url,
contentDescription = null,
error = painterResource(R.drawable.ic_image_placeholder),
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant)
)
}복원력 있는 이미지 컴포넌트
모든 상태를 깔끔하게 처리하는 재사용 가능한 컴포넌트를 조합해 보겠습니다. 로딩 배경, 오류 대체 이미지, 크로스페이드, 자르기 기능을 포함합니다. 이를 목록에 추가하면 빈 공간에 대해 더 이상 걱정하지 않아도 됩니다.
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.request.crossfade
@Composable
fun ResilientImage(url: String?, size: Int = 96) {
val context = LocalContext.current
AsyncImage(
model = ImageRequest.Builder(context)
.data(url)
.crossfade(true)
.build(),
contentDescription = null,
contentScale = ContentScale.Crop,
placeholder = painterResource(R.drawable.placeholder_gray),
error = painterResource(R.drawable.image_broken),
fallback = painterResource(R.drawable.default_avatar),
modifier = Modifier.size(size.dp)
)
}빠른 확인
model = null을 AsyncImage에 전달하면 Coil은 기본적으로 어떤 드로어블을 표시하나요?
복습
이미지 로딩이 자연스럽게 처리되도록 만들었습니다. 핵심 내용은 다음과 같습니다.
- 로딩 중에는
placeholder를 표시하고, 실패하면error를 표시하며, 모델이 null이면fallback을 표시합니다. AsyncImage또는ImageRequest에 설정할 수 있습니다.SubcomposeAsyncImage을 사용하면 상태별로 실제 컴포저블(스피너, 시머)을 지정할 수 있습니다.rememberAsyncImagePainter를 사용하면 원시state에 접근하여 세밀하게 제어할 수 있습니다.- 요청의
listener를 사용하여 로딩 결과를 기록하거나 그에 반응할 수 있습니다.
다음에는 캐싱과 성능을 살펴봅니다. 두 번째부터는 이미지가 즉시 로드되도록 만들어 보겠습니다.
자주 묻는 질문
“자리표시자와 오류 상태” 강의는 무료인가요?
네 — “자리표시자와 오류 상태” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Android Academy 강의 전체를 잠금 해제할 수 있습니다. Android Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“자리표시자와 오류 상태”에서 뭘 배우나요?
로딩과 실패 상황을 자연스럽게 처리합니다. 브라우저에서 직접 실행하는 실습 코드로 Android Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Android Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Android Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.
“자리표시자와 오류 상태” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Android Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Android Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- Coil로 이미지 로드
- 자리표시자와 오류 상태
- 캐싱과 성능
- 오디오와 동영상 재생