React Native

React Native 이미지 Firebase Storage에 업로드하기

Beekei 2022. 3. 13. 21:45
반응형

React Native에서 이미지를 업로드하기 위해선 react-native-image-picker 라이브러리를 사용하면 된다.
이 라이브러리를 사용하면 서진첩에서 이미지를 선택하거나 카메라로 사진을 촬영할 수 있다.

유사한 라이브러리로는 @react-native-community/cameraroll이 있는데, 이 라이브러리는 이미지를 선택하는 UI를 react-native로직으로 직접 만들 수 있다.

1. 라이브러리 설치

먼저 react-native-image-picker 라이브러리를 설치해준다.

$ yarn add react-native-image-picker
$ npx pod-install

2. iOS 설정

ios/프로젝트명/Info.plist 파일 하단에 아래 코드를 추가한다. 이 작업은 카메라 및 갤러리 사용 권한을 위해 필요하다.

    ...
    <key>NSPhotoLibraryUsageDescription</key>
    <string>$(PRODUCT_NAME) would like access to your photo gallery</string>
    <key>NSCameraUsageDescription</key>
    <string>$(PRODUCT_NAME) would like to use your camera</string>
    <key>NSPhotoLibraryAppUsageDescription</key>
    <string>$(PRODUCT_NAME) would like to save photos to your photo gallery</string>
  </dict>
</plist>

3. 안드로이드 설정

안드로이드 설정에서는 android/app/src/main//AndroidManifest.xml 파일에 두 가지 권한을 넣어주어야 한다.
반드시 필요한 작업은 아니지만 카메라로 찍은 사진을 바로 저장하거나 SD카드에 있는 사진을 읽어올 때 필요하다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.프로젝트명">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> // 추가
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> // 추가
    
    ...

위에 작업을 모두 마쳤으면 iOS, 안드로이드 시뮬레이터를 reload 해준다.

$ yarn ios
$ yarn android

4. 라이브러리 사용

react-native-image-picker 라이브러리는 launchCamera와 launchImageLibrary 두 API를 제공한다.

launchCamera(options, callback)

이 API는 사용할 이미지를 카메라로 바로 촬용할 때 사용한다.
options는 생략할 수 있으며, callback은 카메라로 이미지를 선택한 후 호출하는 함수다.

launchImageLibrary(options, callback)

이 API는 갤러리에서 이미지를 선택할 때 사용한다.

아래는 launchCamera와 launchImageLibrary API에 사용되는 options과 callback 항목이다.

options

  • mediaType : photo 또는 video
  • maxWidth : 이미지의 가로 폭을 리사이즈할 때 사용
  • maxHeight : 이미지의 세로 폭을 리사이즈 할 때 사용
  • videoQuality : 영상을 선택할 때 화질을 설정한다.
  • iOS에서는 low, medium, high를, 안드로이드에서는 low, high를 선택할 수 있다.
  • quality : 이미지 화질을 설정(0 ~ 1)
  • includeBase64 : 이 값을 true로 지정하면 이미지를 base64형식으로 인코딩한다.
  • saveToPhotos : launchCamera에서만 사용하는 설정으로 이 값을 true로 설정하면 카메라로 촬영한 후 이미지를 갤러리에 따로 저장한다. 이 옵션을 사용하려면 AndroidManifest.xml에서 WRITE_EXTERNAL_STORAGE 권한을 설정해야 한다.
  • selectionLimit : 선택할 이미지의 수를 설정한다. 기본값은 1이며 0을 넣으면 무제한으로 선택할 수 있다.

callback

  • didCancel : 사용자가 선택을 취소하면 true가 된다.
  • errorCode : 에러에 대한 코드 정보를 지니고 있다. 에러의 종류는 react-native-image-picker GitHub에서 확인할 수 있다.
  • errorMessage : 에러 메세지를 지니고 있다. 개발 과정에서 디버깅할 때만 사용한다.
  • assets : 선택한 이미지의 정보 객체 배열이다. asset 객체는 다음 정보들을 지니고 있다.
  • - base64 : base64로 인코딩 된 이미지 값
  • - uri : 선택한 이미지의 경로
  • - width : 선택한 이미지의 가로 폭
  • - height : 선택한 이미지의 세로 폭
  • - fileSize : 선택한 이미지의 크기
  • - type : 선택한 이미지의 파일 타입
  • - fileName : 선택한 파일의 이름

그럼 launchImageLibrary를 사용해서 갤러리에 있는 이미지를 선택해보자.

import React, { useState } from "react";
import { Pressable, Image } from "react-native";
import { launchImageLibrary } from "react-native-image-picker";
...

function ImageUploadSample() {

  const [response, setResponse] = useState(null);
  const onSelectImage = () => {
    launchImageLibrary(
      {
        mediaType: "photo",
        maxWidth: 512,
        maxHeight: 512,
        includeBase64: Platform.OS === 'android',
      },
      (res) => {
        console.log(res);
        if (res.didCancel) return;
        setResponse(res);
      },
    )
  }
  
  return (
    ...
    <Pressable style={styles.circle} onPress={onSelectImage}>
      <Image style={styles.circle} source={{uri: response?.assets[0]?.uri}} />
    </Pressable>
    ...
  )

}

export default ImageUploadSample;


안드로이드의 경우에는 이미지를 base64로 인코딩해두고, 업로드할 때 base64로 인코딩된 결괏값을 사용해 업로드를 진행해야 한다.(이 권한 이슈는 Google Photo를 사용하는 기기에서 발생)

Pressable을 클릭해 이미지를 선택해보자.

이미지 선택 전 / 이미지 선택 화면 / 이미지 선택 후

5. Firebase Storage에 업로드 하기

Firebase Storage 라이브러리를 설치하는 방법은 [React Native] - React Native 프로젝트에 Firebase 연동하기에 정리되어 있다.
Firebase의 Storage는 무료로 사용할 수 있으며, 총 파일 용량 최대 5GB, 일일 다운로드 최대 1GB의 제한이 있다.

import React, { useState } from "react";
import { Platform, Pressable, Image, Button, ActivityIndicator } from "react-native";
import { launchImageLibrary } from "react-native-image-picker";
...

function ImageUploadSample() {

  const [response, setResponse] = useState(null);
  const onSelectImage = () => {
    launchImageLibrary(
      {
        mediaType: "photo",
        maxWidth: 512,
        maxHeight: 512,
        includeBase64: Platform.OS === 'android',
      },
      (res) => {
        console.log(res);
        if (res.didCancel) return;
        setResponse(res);
      },
    )
  }
  
  const [loading, setLoading] = useState(false);
  const imageUpload = async () => {
    setLoading(true);
    let imageUrl = null;
    if (response) {
        const asset = response.assets[0];
        const reference = storage().ref(`/profile/${asset.fileName}`); // 업로드할 경로 지정
        if (Platform.OS === "android") { // 안드로이드
            // 파일 업로드
            await reference.putString(asset.base64, "base64", {
                contentType: asset.type
            });
        } else { // iOS
            // 파일 업로드
            await reference.putFile(asset.uri);
        }
        imageUrl = response ? await reference.getDownloadURL() : null;
    }
    console.log("imageUrl", imageUrl);
    // imageUrl 사용 로직 ...
  }
  
  return (
    ...
    <Pressable style={styles.circle} onPress={onSelectImage}>
      <Image style={styles.circle} source={{uri: response?.assets[0]?.uri}} />
    </Pressable>
    {loading ? (
      <ActivityIndicator size={32} color="#6200ee" style={styles.spinner} />
    ) : (
      <Button style={styles.button} title="다음" onPress={imageUpload}/>
    )}
    ...
  )

}

export default ImageUploadSample;

위 코드에서 안드로이드일때는 putString을 통해 base64로 인코딩된 테이블로 업로드 하고, iOS일때는 uri에서 파일을 불러와 바로 업로드한다. 이미지 업로드는 보통 1~3초 정도 걸리기 때문에 업로드 하는 동안 ActivityIndicator 컴포넌트로 스피너를 보여주는 것도 좋다.

예제를 실행하고 Firebase Storage에 들어가보면 업로드 한 파일을 확인할 수 있다.



Error: [storage/unauthorized] User is not authorized to perform the desired action
만약 위 코드를 실행하며 위 오류가 발생할때는 Firebase > Storage > Rules에 들어가서 if false로 되어있는 부분을 if true로 변경해주면 정상적으로 작동한다.

반응형