react-native 갤러리 사진 불러오기 (react-native-community/cameraroll 사용)

puka·2022년 9월 14일

react-native-community/cameraroll에 대한 설치는 여기를 참고해주세요.

접근 권한 받기

우선 갤러리에 있는 사진을 불러오기 위해서는 갤러리에 대한 접근 권한을 받아야합니다.

IOS

IOS에서 카메라 롤에 액세스하려면 Info.plist에 NSPhot to LibraryUsageDescription 키를 추가합니다. iOS 11 이상을 실행하는 장치를 대상으로 하는 경우 NSPhotoLibraryAddUsageDescription, NSPhotoLibraryUsageDescription 키를 추가해야 합니다.

Info.plist

<key>NSCameraUsageDescription</key>
<string>카메라 사용 권한은 이미지 촬영 및 첨부를 위해 필요합니다.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>카메라 사용 권한은 이미지 촬영 및 첨부를 위해 필요합니다.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>카메라 사용 권한은 이미지 촬영 및 첨부를 위해 필요합니다.</string>

Android

android는 ios와 다르게 앱에서 권한을 요청하는 기능을 추가해줘야 합니다.
android에서 targetSdkVersion이 29이상일 경우 갤러리나 외부 폴더/파일 접근하는데 오류가 발생할 수 있습니다. 그래서 AndroidManifest.xml에 application에 아래 코드를 추가해야 합니다.

AndroidManifest.xml

<application android:requestLegacyExternalStorage="true">

그리고 안드로이드 경우에는 react-native에서 제공해주는 PermissionsAndroid을 사용합니다.

Gallery.tsx

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

const Gallery = () = {
  const hasAndroidPermission = async () => {
    //외부 스토리지를 읽고 쓰는 권한 가져오기
    const permission = PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE;

    const hasPermission = await PermissionsAndroid.check(permission);
    if (hasPermission) {
      return true;
    };

    const status = await PermissionsAndroid.request(permission);
    return status === 'granted';
  };


  const getPhotoWithPermission = async () => {
    if (Platform.OS === 'android' && !(await hasAndroidPermission())) {
      return;
    };
  };

  useEffect(() => {
    getPhotoWithPermission();
  }, []);

  //생략
  return ()
  
};

export default Gallery;

IOS, Android 둘 다 액세스 권한에 대한 설정은 끝났습니다.

갤러리 데이터 불러오기

import CameraRoll from '@react-native-community/cameraroll';
import RNFS from 'react-native-fs';

//스크롤 될 때마다 사진을 불러올 경우 현재의 갤러리를 어디까지 불러왔는지에 대한 저장 값 
const [ galleryCursor, setGalleryCursor ] = useState(null);
const [ galleryList, setGalleryList ] = useState([]);
const getGalleryPhotos = async () => {
  const params = {
    //이미지를 불러올 개수 (최신순으로)
      first: 50,
      assetType: 'Photos',
      ...galleryCursor && { after: galleryCursor },
   };
  
  try {
    //사진을 불러옵니다. edges는 gallery photo에 대한 정보
      const { edges, page_info } = await CameraRoll.getPhotos(params);
    
      if(page_info.has_next_page === false) {
        setGalleryCursor(null);
      } else {
        setGalleryCursor(page_info.end_cursor);
      }
      
      /*ios인 경우는 ph:// 형식으로 사진이 저장됩니다.
      이미지를 읽을 수 없는 오류가 생기기 때문에
      react-native-fs의 파일 시스템을 이용하여 변환 시켜줍니다.*/
      if (Platform.OS === 'ios') {
        for await (const item of edges) {
          const fileName = item.node.image.uri.replace('ph://', '');
          const result = await this.phPathToFilePath(
            item.node.image.uri,
            fileName,
          );
          item.node.image.uri = result;
        }
      }

      console.log('edges', edges);
      setGalleryList(...galleryList, edges);
    });
  } catch (error) {
     console.log('[takeStore getPhotos error occured] ', error);
  }; 
};

const phPathToFilePath = async (uri) => {
  let fileURI = encodeURI(uri);

  if (uri.startsWith('ph://')) {
    const copyPath = `${RNFS.DocumentDirectoryPath
      }/${new Date().toISOString()}.jpg`.replace(/:/g, '-');

      // ph경로의 이미지를 file로 옮기는 작업
      fileURI = await RNFS.copyAssetsFileIOS(uri, copyPath, 360, 360);
    }

    return fileURI;
};


  const getPhotoWithPermission = async () => {
	...
    //추가
    getPhotos()
  };

  return (
    <View>
      <FlatList
         key="gallery_item"
         data={[...galleryList]}
         keyExtractor={(item, index) => index.toString()}
         style={{
           width: SCREEN_WIDTH,
          }}
          getItemLayout={(data, index) => ({
            length: SCREEN_WIDTH / 3,
            offset: (SCREEN_WIDTH / 3) * (index + 2),
            index,
           })}
           onEndReachedThreshold={0.7}
           numColumns={3}
           nestedScrollEnabled
           howsHorizontalScrollIndicator
           onEndReached={() => {
          //화면의 맨 끝에 도달했을 때 getPhotos 함수 호출
             getPhotos();
           }}
           renderItem={({ item, index }) => {
             return (
               //이미지 보여줄 컴포넌트 (생략)
               <GalleryImageItem
                 item={item}
                 index={index}
                />
            );
          }};
        />
    </View>
  );

프로젝트를 진행하면서 안드로이드에서 사진을 가져오거나 찍는 기능에 있어서 수월하게 진행이 되었는데 IOS에서는 따로 설정을 해줘야 하는 부분을 새롭게 알게 되었습니다.

0개의 댓글