[철학과 개발] #22 적절한 툴을 사용하자! Firebase Storage

in hive-196917 •  5 years ago  (edited)

P & D

Research and Development가 아니라 Philosophy and Development의 P & D 입니다. 개발글에 있어서 새로운 시도를 하고 있습니다. 그저 개발기를 적어 내려가는 것이 아니라, 어떤 철학을 가지고 개발을 해나가고 있는지, 어떤 시도를 했는지, 개발은 어떻게 하는지를 적어 내려갑니다. 이번 시리즈는 도움을 주고 받는 방법 - 이타인클럽의 철학과 개발 과정을 다룰 것입니다.

이전글 - [철학과 개발] #21 초보자의 데이터베이스 구조 설계


철학: 적절한툴을 사용하자

Firebase에서 제공하는 서비스가 참 많습니다. 그 중에서 조금 헷갈릴 수 있는 부분이 Database인 Firestore와 Storage입니다. DB용인 Firestore의 이름때문에 더욱 헷갈립니다. 둘 다 저장하는 것으로 보이니까요.

맞습니다. 둘 다 저장하는 것은 맞습니다. 특히 Storage는 저장하기 위한 용도입니다. 하지만 DB인 Firestore는 단순히 저장한다기 보다는 사용자 정보, 채팅 내용등을 업데이트하고, 쿼리하기 위한 용도가 더 중요합니다.

Storage에 저장된 데이터를 검색해서 빨리 가져오는 용도로는 사용하지 않습니다. Storage에 사용자 아바타 이미지를 저장하고, 이 이미지를 빨리 찾아서(쿼리해서) 표시하기 위해서, Storage에 이미지를 저장하고, 별도로 Firestore에 이미지의 경로를 저장하는 방식을 사용하는 것이 바람직합니다. 왜냐하면 Storage는 쿼리할 수 없기 때문입니다.

이제 Storage와 Firestore가 헷갈리지 않죠?

개발: Storage 사용법

helpus앱의 Storage 구성은 다음과 같습니다.

image.png

여기서는 사용자의 아바타 이미지를 저장하고, 불러들이는 방법을 살펴봅니다.

Firebase가 좋은 점은 Storage를 사용할 때도, Firestore 사용할 때와 유사한 인터페이스를 사용한다는 것입니다. 즉, Firestore에 데이터 저장할 때와 비슷한 방식으로 합니다.

아바타 이미지 업로드

먼저, 사용자가 아바타 이미지를 선택하여 Storage에 업로드 하는 코드를 살펴보겠습니다. 전체 소스코드는 다음을 참고하세요.
https://github.com/EtainClub/helpus

앱에서 이미지를 선택하기 위해 다음과 같은 패키지를 사용합니다.

import ImagePicker from 'react-native-image-picker';

먼저, 이미지를 선택하는 부분입니다. 이미지 피커를 띄우고, 이미지를 선택하면 이미지 경로와 함께 `uploadImage'를 호출합니다.

// AccountEditScreen.js
// user clicks the attachment icon
  updateAvatar = async () => {
    ImagePicker.showImagePicker(pickerOptions, (response) => {
      if (response.didCancel) {
        // alert for canceling avatar picking
        Alert.alert(
          t('AccountEditScreen.cancelPickerTitle'),
          t('AccountEditScreen.cancelPicker'),
          [
            { text: t('confirm') }
          ],
          { cancelable: true },
        );
      } else if (response.error) {
        console.log('AccountEditScreen.pickerError', response.error);
        // alert for avatar picking error
        Alert.alert(
          t('AccountEditScreen.pickerErrorTitle'),
          t('AccountEditScreen.pickerError'),
          [
            { text: t('confirm') }
          ],
          { cancelable: true },
        );
      } else {
        const source = {uri: response.uri};
        console.log('source', source);
        setImgUri(response.uri);
        // upload the avatar image
        uploadImage(source, response.uri);
      }
    });
  }

다음으로 uploadImage함수입니다.

// AccountEditScreen.js
// upload image to firebase storage
  const uploadImage = (source, imageUri) => {
    const ext = imageUri.split('.').pop(); // Extract image extension
    const filename = `${uuid()}.${ext}`; // Generate unique name
//    setImgLoading(true);
    const imgRef = firebase.storage().ref(`avatar/${filename}`);
    const unsubscribe = imgRef.putFile(imageUri)
      .on(
        firebase.storage.TaskEvent.STATE_CHANGED,
        async snapshot => {
          let state = {};
          state = {
            ...state,
            progress: (snapshot.bytesTransferred / snapshot.totalBytes) * 100 // Calculate progress percentage
          };
          if (snapshot.state === firebase.storage.TaskState.SUCCESS) {
            console.log('upload success');
            // unsubscribe the event
            unsubscribe();
            // update the image url
            let url;
            await imgRef.getDownloadURL()
            .then((response) => {
              console.log('get url response', response);
              url = response;
            })
            .catch(error => {
              console.log('Failed to get url', error);
            })
            // update user info, avatar, and name
            console.log('user avatar url', url);
            updateAvatarState({ userId, avatarUrl: url });
            // refresh the screen
            setAvatarUrl(url);
          }
        },
        error => {
          console.log('AccountEditScreen uploading error', error);
          // alert for failure to upload avatar
          Alert.alert(
            t('AccountEditScreen.updateErrorTitle'),
            t('AccountEditScreen.updateError'),
            [
              { text: t('confirm') }
            ],
            { cancelable: true },
          );
        }
      );
  };
  • Storage의 reference를 가져오는 부분은 Firestore와 동일합니다.
    const imgRef = firebase.storage().ref(avatar/${filename});
    위 Storage구조를 보면 최상위 디렉터리 밑에 avatar라는 폴더가 있습니다. imgRef는 그 폴더 밑에 임의로 생성한 이미지 이름을 가리키게 됩니다.

  • putPile함수를 이용하여 이미지를 Storage에 저장합니다.

  • 이미지를 Storage에 저장만 하고 끝나는게 아닙니다. 사용자별로 저장된 이미지의 위치를 DB에 기록해서 필요할 때마다 불러와야 합니다. 그래서 putFile이 이미지 업로드할 때까지 기다립니다.

  • firebase.storage.TaskState.SUCCESS부분에서 업로드가 성공적이면, Storage에 저장된 이미지의 다운로드 경로를 가져옵니다. imgRef.getDownloadURL()

  • 마지막으로 이미지 경로를 제대로 가져오면 사용자 정보(DB)에 저장된 아바타 이미지 경로를 저장합니다.


도움 주고 받는 앱 helpus - V2.1.0

앱 사용자 중 다음과 같은 도움이 가능하 사용자들이 있습니다.

  • 최면 상담
  • 영어, 일본어 관련 문제
  • 건강 상담, 심리 상담

물론, 저도 명상, 루시드 드림 관련 도움을 줄 수 있습니다.

  • 사용법

  • 보다 자세한 내용은 홈페이지를 참고해 주세요.
    https://etain.club

    Authors get paid when people like you upvote their post.
    If you enjoyed what you read here, create your account today and start earning FREE STEEM!