Flutter + Firebase

 

 

 

이번 글에서는 클라우드 DB인 Firestore에서 최신 데이터를 받아 와서 앱 내부의 DB파일에 저장하는 과정에 대해 설명한다.

 

 

앞의 '자동화'에서 최신 영화 데이터를 Firestore에 저장하는 작업을 하루에 한 번씩 한다고 했었다.

여기서 최신 영화 데이터에 대한 의미를 짚고 넘어가야 하는데, 예를 들어 10일에 저장하는 작업을 한다고 치면 최신 영화 데이터는 10일에 나온 영화 데이터가 아닌 3일에 나온 영화데이터를 말한다. 

 

왜 이렇게 정했냐 하면, 등록 분류 일자가 10일이라고 해서 API 및 ors웹 사이트에 10일에 등록된다는 보장이 없기 때문이다.

 

실제로 영화 데이터들은 보통 등록 분류 일자 기준 1~2일 후에 올라왔었고, 많이 밀린다 하면 3~4일까지도 밀렸었다.

(최대로 밀린 경우는 6일이었는데 이건 추석 + 주말 + 임시공휴일 + 개천절이라는 기막힌 연휴라 그랬던 것..)

이렇게 되면 데이터를 호출해도 값이 오지 않아 Firestore이 텅텅 비어버린다.

 

따라서 최신 영화 데이터는 당일 기준 7일 전 영화 데이터라고 정했다.

 

  • 10일에 3일에 등록된 영화데이터 Firestore에 저장  
  • 11일에 4일에 등록된 영화데이터 Firestore에 저장
  • 12일에 5일에 등록된 영화데이터 Firestore에 저장
  • ..... 이런 식으로

 

 

업데이트 과정

앱에서의 DB파일 업데이트 과정은 다음과 같다.

  1. 최근 업데이트 날짜를 가져온다 (마지막으로 업데이트 한 날짜)
  2. 최신 업데이트 날짜를 가져온다 (당일 기준 7일 전 날짜)
  3. 최근 업데이트 날짜부터 최신 업데이트 날짜까지의 영화 데이터를 Firestore에서 가져온다.
  4. 가져온 영화 데이터를 앱 내부 DB에 저장한다.
  5. 최근 업데이트 날짜를 갱신한다.

 

 

1. 최근 업데이트 날짜를 가져온다.

최근 업데이트 날짜는 앱을 종료해도 값이 유지되어야 한다.

따라서 shared_preferences 패키지를 사용하여 최근 업데이트 날짜를 관리한다.

(아래의 패키지는 앱을 종료해도 유지되어야 하는 정보들을 관리할 때 사용하는 패키지다.)

https://pub.dev/packages/shared_preferences

 

shared_preferences | Flutter Package

Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android.

pub.dev

 

 

 

앱의 초기 데이터에는 2023/09/30까지의 영화 데이터가 들어있다.

따라서 앱 처음 실행 시, 최근 업데이트 날짜는 2023/09/30이 된다.

(이 부분은 나중에 initial value setting 설명에 다시 언급할 예정)

 

 

최근 업데이트 날짜를 가져오는 함수

/// 최근 업데이트 년/월/일을 가져옴
Future<DateTime> getLastUpdatedDate() async {
  final SharedPreferences prefs = await SharedPreferences.getInstance();
  int? year = prefs.getInt('updatedYear');
  int? month = prefs.getInt('updatedMonth');
  int? day = prefs.getInt('updatedDay');

  return DateTime(year!, month!, day!);
}

 

 

 

2. 최신 업데이트 날짜를 가져온다.

이건 매우 쉽다. 그냥 현재 날짜에서 7일 빼면 되니..

함수 위에 주석에서 설명했지만, 업데이트 시간이 23시라서 편의상 8일 전 데이터를 최신 데이터로 간주한다.

/// Firebase의 가장 최근 업데이트 날짜를 계산
/// 당일 기준 7일 전 데이터가 가장 최신 데이터이지만, 23시에 업데이트하므로 편의상 8일전 데이터를 최신 데이터로 간주함
DateTime getUpdateDate() {
  DateTime now = DateTime.now();
  DateTime updateDate = DateTime(now.year, now.month, now.day - 8);
  return updateDate;
}

 

 

 

3. 최근 업데이트 날짜부터 최신 업데이트 날짜까지의 영화 데이터를 Firestore에서 가져온다.

우선 Dart&Flutter에서 Firestore를 사용하기 위해서는 cloud_firestore 패키지가 필요하다.

 

https://pub.dev/packages/cloud_firestore

 

cloud_firestore | Flutter Package

Flutter plugin for Cloud Firestore, a cloud-hosted, noSQL database with live synchronization and offline support on Android and iOS.

pub.dev

 

 

cloud_firestore의 사용법은 위의 Example를 봐도 좋고, 아래의 링크의 usage를 한 번 쭉 읽어봐도 좋다.

https://firebase.flutter.dev/docs/firestore/usage/

 

Cloud Firestore | FlutterFire

This page is archived and might not reflect the latest version of the

firebase.flutter.dev

 

 

 

날짜를 인자로 받아 그 날짜에 해당하는 영화 데이터를 가져오는 함수이다.

(컬렉션 ID는 '20231010'과 같은 포맷으로 되어 있음)

/// Firestore에서 영화 데이터들을 가져옴
///
/// DateTime 값을 인자로 받고, 년+월+일(ex 20230930)에 해당하는 컬렉션에서 영화정보들을 가져옴
Future<List<Movie>> getMovieFromFirestore(
    FirebaseFirestore firestore, DateTime date) async {
  List<Movie> movies = [];

  final moviesRef = firestore
      .collection('${date.year}${date.month.toString().padLeft(2, '0')}${date.day.toString().padLeft(2, '0')}')
      .withConverter<Movie>(
        fromFirestore: (snapshot, _) => Movie.fromJson(snapshot.data()!),
        toFirestore: (movie, _) => movie.toJson(), //do not use
      );

  await moviesRef.get().then((snapshot) {
    for (var doc in (snapshot.docs)) {
      movies.add(doc.data());
    }
  });
  return movies;
}

 

 

 

4. 가져온 영화 데이터를 앱 내부 DB에 저장한다.

위의 함수들을 사용하여 최근 업데이트 날짜부터 최신 업데이트 날짜까지의 Firestore 영화 데이터를 가져와 앱 내부 DB에 저장하는 함수이다.

 

/// 영화정보들을 내부 db에 저장
Future<int> updateMovieInfo() async {
  List<Future<List<Movie>>> futures = [];
  int movieCount = 0;
  DateTime lastUpdatedDate = await getLastUpdatedDate(); // year, month, day

  DateTime updateDate = getUpdateDate();

  FirebaseFirestore firestore = FirebaseFirestore.instance;

  // now.compareTo(future); // -1
  // now.compareTo(past); // 1
  // now.compareTo(now); // 0

  while (true) {
    lastUpdatedDate = DateTime(lastUpdatedDate.year, lastUpdatedDate.month, lastUpdatedDate.day + 1);
    if (updateDate.compareTo(lastUpdatedDate) < 0) {
      break;
    }
    futures.add(getMovieFromFirestore(firestore, lastUpdatedDate));
  }

  List<List<Movie>> movieList = await Future.wait(futures);

  MovieDatabase db = MovieDatabase();
  await db.openDB();

  for (List<Movie> movies in movieList) {
    movieCount += movies.length;
    for (Movie movie in movies) {
      db.insertMovie(movie);
    }
  }

  await setLastUpdatedDate(updateDate.year, updateDate.month, updateDate.day);

  return movieCount;
}

 

 

 

5. 최근 업데이트 날짜를 갱신한다.

아래의 함수를 사용해서 최근 업데이트 날짜를 갱신한다.

/// 업데이트 후 년/월/일을 저장
Future<void> setLastUpdatedDate(int year, int month, int day) async {
  final SharedPreferences prefs = await SharedPreferences.getInstance();
  await prefs.setInt('updatedYear', year);
  await prefs.setInt('updatedMonth', month);
  await prefs.setInt('updatedDay', day);

  return;
}

 

 

이렇게 해서 업데이트 기능 구현이 끝났다!

 

 

영화 검색 기능도 구현 완료했고(앞의 글의 DB class에서), 업데이트 기능도 구현이 완료되었으니 핵심 기능들은 모두 구현이 끝났다.

 

이제는 앱 UI와 영화 정보 페이지 그리고 그 외 자잘한 기능들 구현만 남았다. (청불 차단, 광고 삽입, 검색 페이지 등등)

 

위의 작업들은 지금처럼 코드를 올리면 너무 방대해지니 간단간단하게 글을 쓸 예정

(github의 ChildMovie 레포에 소스코드를 올려두었으니 보고 싶으시면 ㄱㄱ)

 

https://github.com/harmlessman/ChildMovie

 

GitHub - harmlessman/ChildMovie

Contribute to harmlessman/ChildMovie development by creating an account on GitHub.

github.com