본문 바로가기
Development/Flutter

Flutter - Firebase Firestore 연동 - 데이터베이스 접근과 읽기/쓰기

by du.it.ddu 2023. 1. 14.
반응형

앱 개발을 할 때 Firebase는 뗄레야 뗄 수 없는 훌륭한 도구다.
Firebase는 많은 도구가 있지만, Firestore는 그 중에서도 정말 유용하게 사용된다.
백엔드를 구축하기 어려운 상황에서 손쉽게 클라우스 데이터베이스를 사용할 수 있기 때문이다.

현재 진행중인 Flutter 프로젝트에서 Firestore 사용이 필요하기 때문에,
연동 과정을 정리한다.

먼저 터미널에서 아래 커맨드로 cloud_firestore 패키지를 추가한다.
flutter pub add cloud_firestore

패키지 추가가 완료되었으므로, Firestore에 접근할 객체를 얻어야 한다.
놀랍게도 굉장히 간단하다. 아래 코드로 객체를 얻는다.

db = FirebaseFirestore.instance;

 

이제 객체를 사용해서 간단한 데이터를 써 본다.

생성할 객체 Map
final user = <String, dynamic>{
  "first": "Ada",
  "last": "Lovelace",
  "born": 1815
};

// users collection에 user를 추가한다.
db.collection("users").add(user).then((DocumentReference doc) =>
    print('DocumentSnapshot added with ID: ${doc.id}'));

객체를 추가할 때는 키 : 값 쌍으로 이루어진 Map 객체를 사용한다.
그리고 collection 이름을 통해 객체를 저장할 collection에 접근하고 add 함수를 사용한다.
객체 생성에 대한 결과는 콜백을 활용하여 확인할 수 있다.

 

이번엔 colleciton에 저장된 데이터를 읽어본다.

await db.collection("users").get().then((event) {
  for (var doc in event.docs) {
    print("${doc.id} => ${doc.data()}");
  }
});

동일하게 collection 객체를 얻은 뒤 get 함수를 사용한다.
async 함수이므로 await을 사용한다.
주의할 부분은 해당 collection의 모든 데이터를 읽어온다는 점이다.
나중에 쿼리를 통해 페이징 처리를 할 수도 있다.

여기서 한 가지 문제는,
읽고 쓰는데 있어서 Map 객체를 활용한다는 부분이다.
읽을 때 Map 객체로부터 클래스를 생성하고, 쓸 때 객체로 부터 Map 객체를 생성해야 한다.
이를 편하게 하기 위해 아래와 같은 방법을 사용할 수 있다.

class City {
  final String? name;
  final String? state;
  final String? country;
  final bool? capital;
  final int? population;
  final List<String>? regions;

  City({
    this.name,
    this.state,
    this.country,
    this.capital,
    this.population,
    this.regions,
  });

   // firestore의 snapshot 으로부터 객체를 생성한다.
  factory City.fromFirestore(
    DocumentSnapshot<Map<String, dynamic>> snapshot,
    SnapshotOptions? options,
  ) {
    final data = snapshot.data();
    return City(
      name: data?['name'],
      state: data?['state'],
      country: data?['country'],
      capital: data?['capital'],
      population: data?['population'],
      regions:
          data?['regions'] is Iterable ? List.from(data?['regions']) : null,
    );
  }

   // 객체로 부터 firestore를 위한 Map 객체를 생성한다.
  Map<String, dynamic> toFirestore() {
    return {
      if (name != null) "name": name,
      if (state != null) "state": state,
      if (country != null) "country": country,
      if (capital != null) "capital": capital,
      if (population != null) "population": population,
      if (regions != null) "regions": regions,
    };
  }
}

factory 생성자를 활용하여 데이터베이스로 부터 읽은 데이터로 객체를 생성하고,
객체를 Map으로 바꾸는 함수를 객체 내에 추가함으로써 해소할 수 있다.

이번엔 이미 생성된 객체를 업데이트 하는 방법을 알아보자.

db.collection("users")
  .doc("frank")
  .update({"age": 13, "favorites.color": "Red"});
  
  db.collection("users")
    .doc("frank")
    .update({
      favorites: {
        food: "Ice Cream"
      }
    })

객체를 쓰는것과 다르지 않다.
다만 함수가 update로 바뀌고, update할 필드에 대한 Map 객체를 넘겨준다.
만약 데이터베이스의 필드가 Nested 하다면, 구조에 맞게 Map 객체를 넘겨주면 된다.

마지막으로, 데이터베이스의 데이터 삭제를 알아보자.

db.collection("cities")
  .doc("DC")
  .delete()
  .then(
      (doc) => print("Document deleted"),
      onError: (e) => print("Error updating document $e"),
   );

이번에도 역시 간단하다.delete 함수를 사용해주면 된다.
collection 안의 document에 대한 ID값을 알아내는 방법은,
객체를 쓰거나 읽었을 때의 DocumentReference의 id를 활용하여 얻을 수 있다.

여기까지 간단한 Firestore의 연동과 읽고쓰기 방법을 알아보았다.
이 외에도 세심한 쿼리 방법과 보안을 위한 규칙 작성 등이 있다.
자세한건 Firestore 공식 문서를 참고하는 것을 권장한다.

https://firebase.google.com/docs/firestore

반응형