블로그 목록
apps

expo-media-library로 사진·영상 갤러리에 저장하기 — 다운로드한 파일이 앱 안에만 갇힐 때

앱에서 이미지를 만들어 저장하는 기능을 붙였다. 캔버스로 그린 그림을 파일로 뽑는 것까진 됐는데, 정작 사용자가 "저장한 사진이 어디 갔냐"고 물었다. expo-file-system으로 앱 전용 디렉토리에 저장했으니 갤러리 앱에서는 안 보이는 게 당연했다. 앱을 지우면 같이 사라지는 것도 문제였다.

기기 갤러리(사진 앱)에 넣으려면 별도 API가 필요하다. Expo에서는 expo-media-library가 그 역할을 한다.

설치와 권한

npx expo install expo-media-library

갤러리에 쓰는 것도 권한이 필요하다. 훅으로 권한 상태를 받아서 없으면 요청하는 흐름이 기본이다.

import * as MediaLibrary from 'expo-media-library';

const [permission, requestPermission] = MediaLibrary.usePermissions();

파일을 갤러리에 저장하기

핵심은 createAssetAsync() 한 줄이다. 로컬 파일 URI를 넘기면 갤러리에 에셋으로 등록해준다. 여기서 넘기는 URI는 원격 URL이 아니라 기기에 이미 내려받은 로컬 경로여야 한다.

async function saveToGallery(localUri: string) {
  if (!permission?.granted) {
    const res = await requestPermission();
    if (!res.granted) return;
  }
  const asset = await MediaLibrary.createAssetAsync(localUri);
  return asset;
}

원격 이미지라면 expo-file-system으로 먼저 다운로드한 뒤 그 로컬 URI를 넘기면 된다. 다운로드한 파일이 앱 안에만 갇히던 문제가 이걸로 풀린다.

앨범으로 묶기

그냥 저장하면 사진 앱 최상단에 흩어진다. 앱 이름으로 앨범을 만들어 모아두면 사용자가 찾기 쉽다.

const asset = await MediaLibrary.createAssetAsync(localUri);
const album = await MediaLibrary.getAlbumAsync('MyApp');

if (album == null) {
  await MediaLibrary.createAlbumAsync('MyApp', asset, false);
} else {
  await MediaLibrary.addAssetsToAlbumAsync([asset], album, false);
}

세 번째 인자 false는 "원본을 복사하지 말고 이동" 옵션인데, 앱이 만든 파일이면 이동으로 두는 게 중복이 안 생긴다.

권한 범위 주의

iOS 14부터는 "선택한 사진만 접근" 권한이 따로 있다. usePermissions({ granular: true })로 요청 시 accessPrivileges를 확인할 수 있는데, 저장만 하는 앱이라면 읽기 권한까지 다 받을 필요 없이 쓰기 전용 권한(writeOnly)만 요청하는 게 심사에서도 깔끔하다.

const [permission, requestPermission] =
  MediaLibrary.usePermissions({ writeOnly: true });

정리하면, 앱이 만든 파일을 사용자에게 "진짜로" 넘겨주려면 createAssetAsync로 갤러리에 등록해야 한다. 딱 필요한 만큼만 권한을 받고, 앨범으로 묶어두면 사용자 경험이 확 달라진다. 파일이 앱 안에 갇혀 있던 문제로 고민 중이라면 이 조합을 먼저 써보길.