React Native

React Native - 스크린샷 활용 (3) 갤러리 저장(@react-native-camera-roll/camera-roll)

크런키스틱 2025. 2. 17. 22:29
728x90

이전 글에 이어서 스크린샷을 갤러리에 저장하는 방법을 살펴본다.

이는 나중에 살펴볼 fs나 file-access 등과 같이 파일 시스템에 접근하여 파일과 디렉토리에 저장하는 것이 아닌 카메라 앨범에 저장하는 방법이다.

 

https://www.npmjs.com/package/@react-native-camera-roll/camera-roll

 

@react-native-camera-roll/camera-roll

React Native Camera Roll for iOS & Android. Latest version: 7.9.0, last published: 3 months ago. Start using @react-native-camera-roll/camera-roll in your project by running `npm i @react-native-camera-roll/camera-roll`. There are 35 other projects in the

www.npmjs.com

 

 

1. 설치

npm install @react-native-camera-roll/camera-roll
#or
yarn add @react-native-camera-roll/camera-roll

 

 

2. Android 권한 설정

android/app/src/main/AndroidManifest.xml 파일에 권한을 추가한다.

// AndroidManifest.xml

<manifest>
...
  <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
  <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:maxSdkVersion="32" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
<application>

 

이 후, 권한을 묻는 코드를 작성한다.

// getCheckPermission.ts

import {PermissionsAndroid, Platform} from 'react-native';

export const hasAndroidPermission = async () => {
  const getCheckPermissionPromise = () => {
    if ((Platform.Version as number) >= 33) {
      return Promise.all([
        PermissionsAndroid.check(
          PermissionsAndroid.PERMISSIONS.READ_MEDIA_IMAGES,
        ),
        PermissionsAndroid.check(
          PermissionsAndroid.PERMISSIONS.READ_MEDIA_VIDEO,
        ),
      ]).then(
        ([hasReadMediaImagesPermission, hasReadMediaVideoPermission]) =>
          hasReadMediaImagesPermission && hasReadMediaVideoPermission,
      );
    } else {
      return PermissionsAndroid.check(
        PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
      );
    }
  };

  const hasPermission = await getCheckPermissionPromise();
  if (hasPermission) {
    return true;
  }
  const getRequestPermissionPromise = () => {
    if ((Platform.Version as number) >= 33) {
      return PermissionsAndroid.requestMultiple([
        PermissionsAndroid.PERMISSIONS.READ_MEDIA_IMAGES,
        PermissionsAndroid.PERMISSIONS.READ_MEDIA_VIDEO,
      ]).then(
        statuses =>
          statuses[PermissionsAndroid.PERMISSIONS.READ_MEDIA_IMAGES] ===
            PermissionsAndroid.RESULTS.GRANTED &&
          statuses[PermissionsAndroid.PERMISSIONS.READ_MEDIA_VIDEO] ===
            PermissionsAndroid.RESULTS.GRANTED,
      );
    } else {
      return PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
      ).then(status => status === PermissionsAndroid.RESULTS.GRANTED);
    }
  };

  return await getRequestPermissionPromise();
};

 

 

3. 코드

// test.tsx

import CustomText from '@/components/text';
import {TestProps} from '@/types/stack';
import {useRef, useState} from 'react';
import {Button, Platform, StyleSheet, View} from 'react-native';
import {SafeAreaView} from 'react-native-safe-area-context';
import ViewShot from 'react-native-view-shot';
import {hasAndroidPermission} from '@/utils/getCheckPermission';
import {showBottomToast} from '@/utils/toastMessage';
import {CameraRoll} from '@react-native-camera-roll/camera-roll';

const TestScreen = ({navigation}: TestProps) => {
  const [uri, setUri] = useState<string>('');

  const viewShotRef = useRef<ViewShot>(null);

  const onCapture = async () => {
    const viewShotUri = await viewShotRef.current?.capture?.();

    if (viewShotUri) setUri(viewShotUri);
  };

  // Save image to gallery
  const onSaveGallery = async (uri: string) => {
    if (Platform.OS === 'android' && !(await hasAndroidPermission())) {
      return showBottomToast('error', '갤러리 접근 권한을 허용해주세요.');
    }

    await CameraRoll.saveToCameraRoll(uri, 'photo');
    showBottomToast('success', '갤러리에 저장되었습니다.');
  };

  return (
    <SafeAreaView className="flex-1">
      <ViewShot
        ref={viewShotRef}
        style={style.viewShot}
        options={{
          fileName: `ScreenShot_${new Date().toDateString()}`,
          format: 'jpg',
          quality: 1,
        }}>
        <View className="w-2/3 h-2/3 flex justify-center items-center bg-white">
          <CustomText className="text-success text-xl" children="테스트 화면" />
          <Button title="캡처" onPress={onCapture} />
          <Button title="저장" onPress={() => onSaveGallery(uri)} />
        </View>
      </ViewShot>
    </SafeAreaView>
  );
};

export default TestScreen;

const style = StyleSheet.create({
  viewShot: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'green',
  },
});

 

간단한 예시 코드로,

  1. 권한 허용 여부를 체크하고 허용되지 않았다면, 토스트 메세지를 띄운다.
  2. 허용된 상태라면 캡처한 이미지 uri를 저장한다.

물론 cameraRoll 라이브러리에는 saveToCameraRoll 말고도 앨범접근, 사진 삭제 등 다양한 methods가 제공되니 필요에 따라 찾아서 사용하면 된다.

 

4. 실행

실행해보면 잘 작동하는 것을 확인할 수 있다.

 

728x90