리스트에 이미지가 많은 화면을 만들다 보면 항상 같은 문제에 부딪힌다. 스크롤할 때마다 이미지가 다시 로딩되면서 깜빡이고, 처음 뜰 때 흰 사각형이 잠깐 보이다가 툭 하고 나타난다. React Native 기본 Image 컴포넌트는 디스크 캐시가 제대로 안 돼서, 같은 URL을 다시 봐도 네트워크를 또 탄다.
이걸 라이브러리로 해결하려고 예전엔 react-native-fast-image를 많이 썼는데, 요즘 New Architecture로 넘어가면서 유지보수가 좀 애매해졌다. 그래서 Expo 프로젝트라면 expo-image가 사실상 정답이다. Expo 팀이 관리하고, New Architecture도 지원한다.
설치
npx expo install expo-image
기본 사용법
Image를 expo-image에서 가져오는 것만으로도 캐싱은 기본으로 켜진다. 여기에 몇 가지 prop만 얹으면 로딩 경험이 확 달라진다.
import { Image } from 'expo-image';
const blurhash = 'L6PZfSi_.AyE_3t7t7R**0o#DgR4';
function Thumbnail({ uri }: { uri: string }) {
return (
<Image
style={{ width: 120, height: 120, borderRadius: 12 }}
source={{ uri }}
placeholder={{ blurhash }}
contentFit="cover"
transition={200}
cachePolicy="memory-disk"
/>
);
}
각 prop이 하는 일이 명확하다.
- placeholder — 이미지가 로드되기 전에 보여줄 것.
blurhash문자열을 넣으면 흐릿한 미리보기가 뜬다. 흰 사각형 대신 실제 이미지의 색감이 미리 깔리니까 훨씬 자연스럽다. - transition — 로딩이 끝나면 몇 ms에 걸쳐 페이드인할지. 툭 튀어나오는 대신 부드럽게 나타난다.
- contentFit — 웹의
object-fit과 같은 개념.cover,contain,fill등. - cachePolicy —
memory-disk가 기본값이다. 메모리와 디스크 둘 다 캐싱하니 앱을 껐다 켜도 다시 안 받는다.
blurhash는 어디서 나오나
blurhash는 이미지를 20~30자 문자열로 압축한 값이다. 보통 서버에서 이미지를 업로드할 때 같이 생성해서 API 응답에 담아준다. 프론트에서 즉석으로 만들 수도 있지만, 리스트 아이템마다 계산하면 낭비라 미리 만들어두는 쪽이 낫다.
캐시 직접 비우기
프로필 이미지처럼 URL은 그대로인데 내용만 바뀌는 경우, 캐시 때문에 옛 이미지가 계속 보일 수 있다. 이럴 땐 명시적으로 비운다.
import { Image } from 'expo-image';
await Image.clearMemoryCache();
await Image.clearDiskCache();
정리하면, 기존 Image를 expo-image로 갈아끼우기만 해도 캐싱은 공짜로 얻는다. 거기에 placeholder와 transition 두 줄만 추가하면 로딩 중 깜빡임이 사라진다. 리스트가 많은 앱이라면 체감 차이가 꽤 크다. blurhash까지 붙이면 데이터가 아직 안 왔는데도 화면이 이미 완성된 것처럼 보인다.