본문 바로가기

[플러터, 구글드라이브] 앱 데이터를 Google Drive에 백업하기&복원하기 2탄

ironwhale 2023. 9. 17.

Flutter와 Google Drive연동하여 백업과 복원 구현하는 방법 2탄입니다. 1탄에서는 구글 드라이브를 사용하기 위한 레포지토리 작업을 마쳤는데요. 이번에는 그 레포지토리를 이용하여 비지니스 로직을 구현해보겠습니다. 이번에는 리버팟을 이용한 상태관리법을 적용해 보았습니다. 


상태 정의하기

코드팩토리님의 중급 강의에서 리버팟을 이용하여 상태를 관리하는 방법을 공부하여 이번 프로젝트에도 적용해 보았습니다. 
우선 각각의 상태를 정의하기 위한 클래스를 만들어 보겠습니다. 각각의 클래스로 로딩중인지 유저 정보가 있는지 여부를 확인하려고 합니다. 이것을 보고 객체지향의 다형성을 이렇게도 활용 가능 하구나 느꼈습니다.  직관적인 이름으로 되어 있으니 쉽게 아실거라 믿고 넘어가겠습니다. 

import 'package:google_sign_in/google_sign_in.dart';

abstract class BaseBackUpModel {
  final String? message;

  BaseBackUpModel({this.message});
}

class BackUpLoading extends BaseBackUpModel {
  BackUpLoading({super.message});
}

class LogoutModel extends BaseBackUpModel {
  LogoutModel({super.message});
}

class LoginModel extends BaseBackUpModel {
  GoogleSignInAccount googleUser;

  LoginModel({super.message, required this.googleUser});
}

class BackUpFileExist extends LoginModel {
  BackUpFileExist({super.message, required super.googleUser});
}

class BackUpFileNotExist extends LoginModel {
  BackUpFileNotExist({super.message, required super.googleUser});
}

class BackUpping extends LoginModel {
  BackUpping({super.message, required super.googleUser});
}

class BackUpEnd extends LoginModel {
  BackUpEnd({super.message, required super.googleUser});
}

class Restoring extends LoginModel {
  Restoring({super.message, required super.googleUser});
}

class RestoreEnd extends LoginModel {
  RestoreEnd({super.message, required super.googleUser});
}

class ErrorModel extends BaseBackUpModel {
  ErrorModel({super.message});
}

리버팟으로 로그인, 로그아웃 상태 구현하기 

이제 본격적으로 리버팟을 사용하여 비지니스 로직을 구현해 보겠습니다. 구현 구글 드라이브를 사용하기 위해 로그인을 해보겠습니다. 
 
이전에 만든 리포지토리는 외부에서 주입 받고 구글 로그인 정보는 따로 필드값으로 선언했습니다. 

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:moor_train/backup/model/backup_model.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:moor_train/backup/repository/back_repository.dart';

class BackUpNotifier extends StateNotifier<BaseBackUpModel> {
  GoogleSignInAccount? googleUser;
  final BackUpRepository repository;

  BackUpNotifier({required this.repository}) : super(BackUpLoading()) {}

  Future<void> signIn() async {
    googleUser = await repository.signIn();

    if (googleUser == null) {
      state = LogoutModel(message: "로그인을 해주세요");
      return;
    }
    state = LoginModel(googleUser: googleUser!);
  }

  Future<void> signOut() async {
    await repository.signOut();
    state = LogoutModel(message: "로그인을 해주세요");
  }
}

구글 드라이브에 저장된 파일 정보 가져오기

기본적으로 로그인이 되면 구글드라이브에 저장된 파일 정보를 가져오도록 하는 로직을 구현하겠습니다. 다형성을 이용해서 여러가지 상태를 리버팟으로 관리하는 모습을 보실수 있습니다. 코드팩토리님의 중급 강의에 이것을 보고 저는 혁명적이라 생각했습니다. 
 
로그인 하는 메소드에 paginate() 를 실행 시켜 추후 화면에 백업된 파일이 있으면 표시 할수 있도록 했습니다. 

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:moor_train/backup/model/backup_model.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:moor_train/backup/repository/back_repository.dart';

class BackUpNotifier extends StateNotifier<BaseBackUpModel> {
  GoogleSignInAccount? googleUser;
  final BackUpRepository repository;

  BackUpNotifier({required this.repository}) : super(BackUpLoading()) {
    if (googleUser == null) {
      state = LogoutModel(message: "로그인을 해주세요");
    } else if (googleUser != null) {
      state = LoginModel(googleUser: googleUser!);
      paginate();
    }
  }

생략

  Future<void> signIn() async {
    googleUser = await repository.signIn();

    if (googleUser == null) {
      state = LogoutModel(message: "로그인을 해주세요");
      return;
    }
    state = LoginModel(googleUser: googleUser!);
    paginate();
  }


  Future<void> paginate() async {
    try {
      if (googleUser == null) {
        return;
      }
      state = LoginModel(googleUser: googleUser!);
      final driveApi = await repository.getDriveApi(googleUser!);
      final file = await repository.getDriveFile(
          driveApi: driveApi!, filename: "db.sqlite");

      String dateTime = "";
      DateFormat dateFormat = DateFormat('yy/MM/dd');

      if (file == null) {
        state = BackUpFileNotExist(googleUser: googleUser!);
      } else {
        if (file.modifiedTime != null) {
          dateTime = dateFormat.format(file.modifiedTime!);
        } else if (file.createdTime != null) {
          dateTime = dateFormat.format(file.createdTime!);
        }
      }
      state = BackUpFileExist(
          message: "$dateTime에 백업된 파일이 있습니다. ", googleUser: googleUser!);
    } catch (e) {
      state = ErrorModel(message: e.toString());
    }
  }
}

 

구글 드라이브에 백업하고 복원하는 로직 구현

이제 구글 드라이브에 파일을 저장하고 앱에 다시 다운 받는 로직을 구현하겠습니다. 
 

Future<void> backUpDb() async {
    try {
      if (googleUser == null) {
        LogoutModel(message: "로그인 해주세요");
        return;
      }
      final dbFolder = await getApplicationDocumentsDirectory();
      final dbPath = join(dbFolder.path, "db.sqlite");
      final File selectedFile = File(dbPath);

      final driveApi = await repository.getDriveApi(googleUser!);
      final driveFile = await repository.getDriveFile(
          driveApi: driveApi!, filename: "db.sqlite");

      state = BackUpping(googleUser: googleUser!);
      await repository.upLoad(
          driveApi: driveApi, file: selectedFile, driveFileId: driveFile?.id);
      state = BackUpEnd(googleUser: googleUser!);
    } catch (e) {
      state = ErrorModel(message: e.toString());
    }

    // 복원 하는 부분
  }

  Future<void> restoreDB() async {
    try {
      Directory dbFolder = await getApplicationDocumentsDirectory();
      final String path = join(dbFolder.path, 'db.sqlite');

      if (googleUser == null) {
        return;
      }

      state = Restoring(googleUser: googleUser!);

      final driveApi = await repository.getDriveApi(googleUser!);

      final file = await repository.getDriveFile(
          driveApi: driveApi!, filename: "db.sqlite");
      if (file == null) {
        state = ErrorModel(message: "Back Up File not Exist");
        return;
      }

      await repository.downLoad(
          driveApi: driveApi, driveFileId: file.id!, localPath: path);

      state = RestoreEnd(googleUser: googleUser!);
    } catch (e) {
      state = ErrorModel(message: e.toString());
    }
  }

전체 프로바이더 코드

import 'dart:io';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:moor_train/backup/model/backup_model.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:moor_train/backup/repository/back_repository.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';

final backUpProvider =
    StateNotifierProvider<BackUpNotifier, BaseBackUpModel>((ref) {
  final repository = ref.watch(backUpRepositoryProvider);
  return BackUpNotifier(repository: repository);
});

class BackUpNotifier extends StateNotifier<BaseBackUpModel> {
  GoogleSignInAccount? googleUser;
  final BackUpRepository repository;

  BackUpNotifier({required this.repository}) : super(BackUpLoading()) {
    if (googleUser == null) {
      state = LogoutModel(message: "로그인을 해주세요");
    } else if (googleUser != null) {
      state = LoginModel(googleUser: googleUser!);
      paginate();
    }
  }

  Future<void> signIn() async {
    googleUser = await repository.signIn();

    if (googleUser == null) {
      state = LogoutModel(message: "로그인을 해주세요");
      return;
    }
    state = LoginModel(googleUser: googleUser!);
    paginate();
  }

  Future<void> signOut() async {
    await repository.signOut();
    state = LogoutModel(message: "로그인을 해주세요");
  }

  Future<void> paginate() async {
    try {
      if (googleUser == null) {
        return;
      }
      state = LoginModel(googleUser: googleUser!);
      final driveApi = await repository.getDriveApi(googleUser!);
      final file = await repository.getDriveFile(
          driveApi: driveApi!, filename: "db.sqlite");

      String dateTime = "";
      DateFormat dateFormat = DateFormat('yy/MM/dd');

      if (file == null) {
        state = BackUpFileNotExist(googleUser: googleUser!);
      } else {
        if (file.modifiedTime != null) {
          dateTime = dateFormat.format(file.modifiedTime!);
        } else if (file.createdTime != null) {
          dateTime = dateFormat.format(file.createdTime!);
        }
      }
      state = BackUpFileExist(
          message: "$dateTime에 백업된 파일이 있습니다. ", googleUser: googleUser!);
    } catch (e) {
      state = ErrorModel(message: e.toString());
    }
  }

  Future<void> backUpDb() async {
    try {
      if (googleUser == null) {
        LogoutModel(message: "로그인 해주세요");
        return;
      }
      final dbFolder = await getApplicationDocumentsDirectory();
      final dbPath = join(dbFolder.path, "db.sqlite");
      final File selectedFile = File(dbPath);

      final driveApi = await repository.getDriveApi(googleUser!);
      final driveFile = await repository.getDriveFile(
          driveApi: driveApi!, filename: "db.sqlite");

      state = BackUpping(googleUser: googleUser!);
      await repository.upLoad(
          driveApi: driveApi, file: selectedFile, driveFileId: driveFile?.id);
      state = BackUpEnd(googleUser: googleUser!);
    } catch (e) {
      state = ErrorModel(message: e.toString());
    }

    // 복원 하는 부분
  }

  Future<void> restoreDB() async {
    try {
      Directory dbFolder = await getApplicationDocumentsDirectory();
      final String path = join(dbFolder.path, 'db.sqlite');

      if (googleUser == null) {
        return;
      }

      state = Restoring(googleUser: googleUser!);

      final driveApi = await repository.getDriveApi(googleUser!);

      final file = await repository.getDriveFile(
          driveApi: driveApi!, filename: "db.sqlite");
      if (file == null) {
        state = ErrorModel(message: "Back Up File not Exist");
        return;
      }

      await repository.downLoad(
          driveApi: driveApi, driveFileId: file.id!, localPath: path);

      state = RestoreEnd(googleUser: googleUser!);
    } catch (e) {
      state = ErrorModel(message: e.toString());
    }
  }
}

이제 구글 드라이브에 앱 데이터를 저장하고 다운로드 받는 것은 이것으로 마치겠습니다. 이것만 아신다면 이제 별도의 비용없이 사용자의 구글 드라이브를 이용하여 데이터를 저장하고 다운받는 것을 하실수 있으실 겁니다. 
 

댓글