꿈꾸는 시스템 디자이너

Flutter 강좌 - [Firebase] Cloud Storage 사용법 본문

Development/Flutter

Flutter 강좌 - [Firebase] Cloud Storage 사용법

독행소년 2019. 12. 18. 13:07

Flutter Code Examples 강좌를 추천합니다.

  • 제 블로그에서 Flutter Code Examples 프로젝트를 시작합니다.
  • Flutter의 다양한 예제를 소스코드와 실행화면으로 제공합니다.
  • 또한 모든 예제는 Flutter Code Examples 앱을 통해 테스트 가능합니다.

Flutter Code Examples 강좌로 메뉴로 이동

Flutter Code Examples 강좌 목록 페이지로 이동

Flutter Code Examples 앱 설치 | Google Play Store로 이동

 

Flutter Code Examples - Google Play 앱

Are you a beginner at Flutter? Check out the various features of Flutter through the demo. Source code for all demos is also provided.

play.google.com


Flutter 강좌 시즌2 목록 : https://here4you.tistory.com/149

 

이번 강좌에서는 Firebase에서 제공하는 기능 중 하나인 Cloud Storage에 대해서 알아본다.

Cloud Storage(이하 Storage)는 이름 그래도 클라우드 환경의 파일 저장소다. 서비스를 이용하는 사용자의 사진, 문서, 동영상 등의 파일들을 업로드하고 다운로드할 수 있다. 더욱이 업로드된 파일의 URL을 제공하여 접근이 가능하기 때문에 앱에서 사용자의 프로필 사진을 Storage로 업로드한 후 업로드된 프로필 사진의 URL를 이용해서 화면을 구성할 수 있다.

 

이번 강좌도 지난 Flutter Firebase 앱을 이용하여 진행하므로 이전 강좌 포스팅에 대한 선이해가 필요하다.

 

1. Firebase Storage 환경 구성

Firebase 콘솔에 진입하여 Storage 메뉴에서 시작하기를 선택한다.

 

첫 번째로 보안 규칙에 대해 설명된다. 다음을 선택한다.

 

다음으로 이용할 Storage의 위치 설정 화면이 나타난다. 기존에 Firestore를 설정하면서 지정된 위치를 이용하게 된다. 완료를 선택한다.

 

설정이 완료되면 다음과 같이 화면이 나타난다.

 

프로필 사진을 업로드할 폴더 profile을 생성하자.

 

profile 폴더가 생성되었다.

 

보안 규칙(Rules) 메뉴로 이동해보자.

기본값으로, request.auth 값이 null 이 아니면 모든 경로에 대해 read, write가 가능하도록 설정되어 있다. 즉 Firebase Authentication 기능으로 통해 로그인된 사용자라면 모든 경로에서 읽고 쓰기가 가능하다는 뜻이다.

 

2. Flutter Firebase 앱 수정 #1 - 이미지 피커 플러그인을 이용한 프로필 사진 선택

Storage를 이용하기 전에 프로필 사진으로 이용할 사진을 선택하는 기능을 구현해 보자. pubspec.yaml 파일에 이미지 피커 플러그인을 추가한 후 Packages get을 실행하자.

dependencies:
  image_picker: ^0.6.2+2

2019년 12월 18일 현재 image_picker 플러그인은 0.6.2+3 버전까지 출시되었다. 그런데 이 버전은 Flutter SDK 1.12.13 버전을 요구하며 기존의 Flutter Firebase 앱의 SDK 보다 상위 버전이다. SDK를 최신 버전으로 업그레이드한 후 진행할 수도 있으나 그럴 경우 다른 플러그인과의 버전 충돌이 발생할 수 있으므로 기존의 SDK를 지원하는 0.6.2+2 버전을 이용한다. Flutter SDK의 업그레이드와 다운그레이드 방법에 대해서는 기존 강좌 포스팅을 참고한다.

 

다음과 같이 새로운 페이지를 구현한다.

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';

CloudStorageDemoState pageState;

class CloudStorageDemo extends StatefulWidget {
  @override
  CloudStorageDemoState createState() {
    pageState = CloudStorageDemoState();
    return pageState;
  }
}

class CloudStorageDemoState extends State<CloudStorageDemo> {
  File _image;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Cloud Storage Demo")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            CircleAvatar(
              backgroundImage:
                  (_image != null) ? FileImage(_image) : NetworkImage(""),
              radius: 30,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                RaisedButton(
                  child: Text("Gallery"),
                  onPressed: () {
                    _uploadImageToStorage(ImageSource.gallery);
                  },
                ),
                RaisedButton(
                  child: Text("Camera"),
                  onPressed: () {
                    _uploadImageToStorage(ImageSource.camera);
                  },
                )
              ],
            )
          ],
        ),
      ),
    );
  }

  void _uploadImageToStorage(ImageSource source) async {
    File image = await ImagePicker.pickImage(source: source);

    if (image == null) return;
    setState(() {
      _image = image;
    });
  }
}

이미지 피커 플러그인을 이용하면, 갤러리에서 사진을 선택하거나 카메라를 통해 새로운 사진을 촬영하여 프로필 이미지를 획득할 수 있다.

실행 화면은 다음과 같다.

 

3. Flutter Firebase 앱 수정 #2 - Storage 플러그인을 이용하여 이미지 업로드 기능 구현

pubspec.yaml 파일에 Storage 플러그인을 추가한다.

dependencies:
  firebase_storage: ^3.0.10

이번 플러그인 역시 최신 버전이 아닌 기존 Flutter Firebase에서 사용하는 SDK와 호환되는 버전을 이용했다.

 

그리고 코드를 다음과 같이 수정한다.

import 'dart:io';

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:firebase_storage/firebase_storage.dart';

CloudStorageDemoState pageState;

class CloudStorageDemo extends StatefulWidget {
  @override
  CloudStorageDemoState createState() {
    pageState = CloudStorageDemoState();
    return pageState;
  }
}

class CloudStorageDemoState extends State<CloudStorageDemo> {
  File _image;
  FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
  FirebaseUser _user;
  FirebaseStorage _firebaseStorage = FirebaseStorage.instance;
  String _profileImageURL = "";

  @override
  void initState() {
    super.initState();
    _prepareService();
  }

  void _prepareService() async {
    _user = await _firebaseAuth.currentUser();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Cloud Storage Demo")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            // 업로드할 이미지를 출력할 CircleAvatar
            CircleAvatar(
              backgroundImage:
                  (_image != null) ? FileImage(_image) : NetworkImage(""),
              radius: 30,
            ),
            // 업로드할 이미지를 선택할 이미지 피커 호출 버튼
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                RaisedButton(
                  child: Text("Gallery"),
                  onPressed: () {
                    _uploadImageToStorage(ImageSource.gallery);
                  },
                ),
                RaisedButton(
                  child: Text("Camera"),
                  onPressed: () {
                    _uploadImageToStorage(ImageSource.camera);
                  },
                )
              ],
            ),
            Divider(
              color: Colors.grey,
            ),
            // 업로드 된 이미지를 출력할 CircleAvatar
            CircleAvatar(
              backgroundImage: NetworkImage(_profileImageURL),
              radius: 30,
            ),
            // 업로드 된 이미지의 URL
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: Text(_profileImageURL),
            )
          ],
        ),
      ),
    );
  }

  void _uploadImageToStorage(ImageSource source) async {
    File image = await ImagePicker.pickImage(source: source);

    if (image == null) return;
    setState(() {
      _image = image;
    });

    // 프로필 사진을 업로드할 경로와 파일명을 정의. 사용자의 uid를 이용하여 파일명의 중복 가능성 제거
    StorageReference storageReference =
        _firebaseStorage.ref().child("profile/${_user.uid}");

    // 파일 업로드
    StorageUploadTask storageUploadTask = storageReference.putFile(_image);

    // 파일 업로드 완료까지 대기
    await storageUploadTask.onComplete;

    // 업로드한 사진의 URL 획득
    String downloadURL = await storageReference.getDownloadURL();

    // 업로드된 사진의 URL을 페이지에 반영
    setState(() {
      _profileImageURL = downloadURL;
    });
  }
}

소스코드를 살펴보면,

우선 업로드할 프로필 이미지의 파일명을 결정해야 한다. 중복된 파일명이 발생하지 않으려면 유니크한 파일명이 필요한데 쉽게 이용할 수 있는 것이 사용자의 UID 값이다. 이 값은 지난 강좌에서 다룬 Fireabse 인증의 uid를 이용한다.

화면 구성은 기존 코드에서 업로드한 이미지를 출력할 CircleAvatar 위젯과 업로드한 이미지의 URL을 출력할 Text 위젯을 추가했다.

_uploadImageToStorage 메서드에서는 이미지 피커를 통해 업로드할 이미지를 선택하는 기존 코드에 선택된 이미지를 업로드하고 업로드한 이미지의 URL를 획득하는 코드가 추가되었다.

 

실행화면은 다음과 같다.

.

 

이번 강좌에서는 Firebase의 Cloud Storage의 사용법에 대해서 알아봤다. Storage를 이용하면 쉽게 파일을 업로드하고 업로드한 파일의 URL을 이용해서 쉽게 접근이 가능하다.

 

Comments