본문 바로가기

Flutter-플러터/플러터 공부

Flutter - supabase를 활용한 가계부 백업 기능 구현

반응형
문제 해결 목적 

 

supabase storage를 활용한 가계부 sqlite db 파일 백업 기능 구현 

 

 

문제 해결 과정

 

2024.02.09 - [Flutter-플러터/플러터 공부] - 7external_path 패키지, 결국 supabase

 

7external_path 패키지, 결국 supabase

https://pub.dev/packages/external_path external_path | Flutter package A flutter plugin to get internal, external storage and external public directory path. pub.dev 문제 해결 목적 백업 서비스를 구현 중 입니다. 원래계획은 앱 외부

ownerdev88.tistory.com

 

원래 목표는 안드로이드 - google drive , IOS - icloud 를 이용한 백업 서비스를 생각했었습니다. 

하지만 편한가계부 네이버 까페에 가보면 안드로이드 폰 -> 아이폰으로 변경한 고객님께서 데이터 백업 절차가 복잡하다는 글이 좀 있더라구요. 

 

그래서 결국 supabase에 있는 storage 를 이용해서 백업 기능을 구현하게 되었습니다. 

유데미에 있는 아래 강좌를 이용해서 구현했습니다. 

 

 

 

SupabaseService 코드 

static으로 각 함수를 바로 사용할 수 있게 했습니다. 

supabase storage의 기본적인 사용법은 다음과 같습니다. 

 

1. supabase storage 에서 bucket 을 만든다. 

2. bucket의 정책을 설정한다.  supabase 로그인 된 아이디 = auth.id 이런 정책

3. 이렇게 되면 supabase bucket은 각 유저의 아이디로 생성된 폴더가 생깁니다. 그 폴더 안에 유저의 db를 보관하는 시스템입니다. 

 

4. supabase에 있는 db파일을 다시 앱에 적용시키기 위해서는 앱을 재시작 해야 합니다.  아래 패키지를 사용했습니다. 좋네요!!!

 

https://pub.dev/packages/restart_app

 

restart_app | Flutter package

A Flutter plugin that helps you to restart the whole Flutter app with a single function call by using native APIs.

pub.dev

import 'dart:io';

import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:money_tracker_test/model/needs/needs_model.dart';
import 'package:money_tracker_test/model/user/delete_user_model.dart';
import 'package:path_provider/path_provider.dart';

import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:restart_app/restart_app.dart';

class SupabaseService {
  // Supabase 클라이언트를 초기화하는 메소드

  // 데이터 생성 (Create)
  static Future<void> createNeed(Needs need) async {
    final supabase = Supabase.instance.client;
    try {
      await supabase.from('needs').upsert([need.toJson()]);
    } catch (e) {
      // 예외 발생 시 로그 출력 또는 오류 처리
      print('Supabase 데이터 생성 중 오류 발생: $e');
    }
  }

  static Future<void> createdDeleteUser(DeleteUser deleteUser) async {
    final supabase = Supabase.instance.client;
    try {
      await supabase.from("delete_user_confirm").upsert([deleteUser.toJson()]);
    } catch (e) {
      print('Supabase 데이터 생성 중 오류 발생: $e');
    }
  }

  // 안드로이드에서 파일 경로를 가져오는 함수
  static Future<String> getDatabasePathAndroid() async {
    return "/data/user/0/com.jalam.dev.app/databases/moneytracker.db";
  }

  // iOS에서 파일 경로를 가져오는 함수
  static Future<String> getDatabasePathIOS() async {
    Directory appDocDir = await getApplicationDocumentsDirectory();
    String appDocPath = appDocDir.path;
    return '$appDocPath/moneytracker.db';
  }

  // 플랫폼에 따라 파일 경로를 가져오는 함수
  static Future<String> getDatabasePath() async {
    if (Platform.isAndroid) {
      return getDatabasePathAndroid();
    } else if (Platform.isIOS) {
      return getDatabasePathIOS();
    } else {
      throw UnsupportedError('이 플랫폼은 지원되지 않습니다.');
    }
  }

//db 파일에 supabase storage 에 upload
  static Future<String?> uploadDatabaseToStorage() async {
    final supabase = Supabase.instance.client;
    try {
      String dbPath = await getDatabasePath();
      File dbFile = File(dbPath);

      final now = DateTime.now();
      final formatter = DateFormat('yy.MM.dd.HHmmss');
      final String formattedDate = formatter.format(now);
      final String fileName = "${formattedDate}_moneytracker.db";

      if (await dbFile.exists()) {
        // 파일이 존재하면 업로드
        await supabase.storage
            .from('db_data')
            .upload("${supabase.auth.currentUser!.id}/$fileName", dbFile);
        print('Database uploaded to Supabase Storage');
      } else {
        print('Database file does not exist');
      }
    } catch (e) {
      print('Error uploading database to Supabase Storage: $e');
    }
    return null;
  }

//supabase storage에 있는 파일을 리스트화 ui

  static Future getData() async {
    final supabase = Supabase.instance.client;
    final List<FileObject> result = await supabase.storage
        .from("db_data")
        .list(path: supabase.auth.currentUser!.id);

    final List<Map<String, String>> mydata = [];

    if (result.isEmpty) {
      // 데이터가 없는 경우 빈 리스트 반환
      return mydata;
    } else {
      for (var data in result) {
        mydata.add({
          "파일 이름": data.name,
        });
      }
    }

    print(mydata);
    return mydata;
  }

  //supabase storage 삭제 기능

  static Future<void> deleteData(String dataName) async {
    final supabase = Supabase.instance.client;
    try {
      supabase.storage
          .from("db_data")
          .remove([supabase.auth.currentUser!.id + "/" + dataName]);
    } catch (e) {
      print('Error deleting database to Supabase Storage: $e');
    }
  }

//supabase storage에 있는 백업 파일을 누르면 restore

  static Future<void> restoreDatabase(String fileName) async {
    final supabase = Supabase.instance.client;
    try {
      // 로컬에서 데이터베이스 파일 경로 가져오기
      final String dbPath = await getDatabasePath();

      // Supabase Storage에서 파일 다운로드
      final fileResponse = await supabase.storage
          .from('db_data')
          .download('${supabase.auth.currentUser!.id}/$fileName');

      // 다운로드된 파일 저장
      final File dbFile = File(dbPath);
      await dbFile.writeAsBytes(fileResponse);

      Restart.restartApp();

      print('Database restored from Supabase Storage');
    } catch (e) {
      Get.snackbar('복원 실패', '백업 파일 복원 중 오류가 발생했습니다.');
      print('Error restoring database from Supabase Storage: $e');
    }
  }
}

 

 

결과

 

 

1번 영상 

IOS 시뮬레이터에서 백업 기능 구현 

 

 

2번 영상 

IOS 시뮬레이터에서 백업한 파일을 안드로이드 폰에서 다시 백업 진행 

 

 

IOS 에서 생성된 데이터 ->  안드로이드에서 정상 작동 확인!

728x90
반응형