[Firebase] Cloud Firestore - CRUD - 1

박지훈·2023년 11월 22일

Firebase

목록 보기
2/6
post-thumbnail

Cloud Firestore - CRUD

데이터 추가 및 업데이트

  • 문서 식별자를 명시적으로 지정하여 컬렉션 내의 문서 데이터를 설정.
  • 컬렉션에 새 문서를 추가. 이 경우 Cloud Firestore에서 자동으로 문서 식별자를 생성.
  • 자동으로 생성된 식별자로 빈 문서를 만들고 나중에 데이터를 할당.

Cloud Firestore 초기화

Cloud Firestore 인스턴스를 초기화

db = FirebaseFirestore.instance;

문서 설정

단일 문서를 만들거나 덮어쓰려면 set() 메서드를 사용

final city = <String, String>{
  "name": "Los Angeles",
  "state": "CA",
  "country": "USA"
};

db
    .collection("cities")
    .doc("LA")
    .set(city)
    .onError((e, _) => print("Error writing document: $e"));

문서가 없으면 생성된다. 문서가 있으면 새로 제공한 데이터로 내용을 덮어쓰지만, 다음과 같이 데이터를 기존 문서와 병합하도록 지정한 경우는 예외이다.

// Update one field, creating the document if it does not already exist.
final data = {"capital": true};

db.collection("cities").doc("BJ").set(data, SetOptions(merge: true));

데이터 유형

Cloud Firestore를 사용하면 문서 안에 문자열, 불리언, 숫자, 날짜, null, 중첩 배열, 객체를 비롯하여 다양한 데이터 유형을 쓸 수 있다.
Cloud Firestore는 코드에서 사용하는 숫자 유형과 관계없이 숫자를 항상 배정밀도 부동 소수점으로 저장한다.

final docData = {
  "stringExample": "Hello world!",
  "booleanExample": true,
  "numberExample": 3.14159265,
  "dateExample": Timestamp.now(),
  "listExample": [1, 2, 3],
  "nullExample": null
};

final nestedData = {
  "a": 5,
  "b": true,
};

docData["objectExample"] = nestedData;

db
    .collection("data")
    .doc("one")
    .set(docData)
    .onError((e, _) => print("Error writing document: $e"));

커스텀 객체

Map 또는 Dictionary 객체로는 문서를 표현하기가 불편한 경우가 많으므로 Cloud Firestore는 커스텀 클래스를 사용하여 문서를 작성하는 방식도 지원한다.
Cloud Firestore에서 객체를 지원되는 데이터 유형으로 변환한다.
커스텀 클래스를 사용하면 앞의 예시를 다음과 같이 다시 작성할 수 있다.

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,
  });

  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,
    );
  }

  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,
    };
  }
}

💡 아래 예시와 같이 withConverter를 사용하면 상당히 편리하게 사용가능했다!

final city = City(
  name: "Los Angeles",
  state: "CA",
  country: "USA",
  capital: false,
  population: 5000000,
  regions: ["west_coast", "socal"],
);
final docRef = db
    .collection("cities")
    .withConverter(
      fromFirestore: City.fromFirestore,
      toFirestore: (City city, options) => city.toFirestore(),
    )
    .doc("LA");
await docRef.set(city);

문서 추가

기본적으로 set()을 사용하여 문서를 만들 때는 만들 문서의 ID를 지정해야 한다. 예를 들면 다음과 같다.

db.collection("cities").doc("new-city-id").set({"name": "Chicago"});

그러나 문서에 유의미한 ID를 두지 않고 Cloud Firestore에서 자동으로 ID를 생성하도록 하는 것이 편리한 때도 있다. 이렇게 하려면 다음과 같은 add() 메서드를 호출하면 된다.

// Add a new document with a generated id.
final data = {"name": "Tokyo", "country": "Japan"};

// add method
db.collection("cities").add(data).then((documentSnapshot) =>
    print("Added Data with ID: ${documentSnapshot.id}"));

💡 중요: Firebase 실시간 데이터베이스의 '푸시 ID'와 달리, Cloud Firestore에서 자동으로 생성한 ID에서는 자동 정렬을 지원하지 않는다. 생성일에 따라 문서를 정렬하려면 타임스탬프를 문서의 필드로 저장해야 한다.

경우에 따라서는 자동 생성 ID를 사용하여 문서 참조를 만든 후 참조를 사용하는 방법이 유용할 수 있다. 다음과 같이 doc()을 호출하면 된다.

실제로 .add(...).doc().set(...)은 완전히 동일하므로 더 편리한 것을 사용하면 된다.

// Add a new document with a generated id.
final data = <String, dynamic>{};

final newCityRef = db.collection("cities").doc();

// Later...
newCityRef.set(data);

문서 업데이트

전체 문서를 덮어쓰지 않고 문서의 일부 필드를 업데이트하려면 update() 메서드를 사용.

final washingtonRef = db.collection("cites").doc("DC");
washingtonRef.update({"capital": true}).then(
    (value) => print("DocumentSnapshot successfully updated!"),
    onError: (e) => print("Error updating document $e"));

서버 타임스탬프

문서의 필드를 서버 업데이트 수신 시점을 추적하는 서버 타임스탬프로 설정할 수 있다.

final docRef = db.collection("objects").doc("some-id");
final updates = <String, dynamic>{
  "timestamp": FieldValue.serverTimestamp(),
};

docRef.update(updates).then(
    (value) => print("DocumentSnapshot successfully updated!"),
    onError: (e) => print("Error updating document $e"));

중첩된 객체의 필드 업데이트

문서에 중첩된 객체가 있으면 update()를 호출할 때 '점 표기법'을 사용하여 문서 내 중첩 필드를 참조할 수 있다.

// Assume the document contains:
// {
//   name: "Frank",
//   favorites: { food: "Pizza", color: "Blue", subject: "recess" }
//   age: 12
// }
db
    .collection("users")
    .doc("frank")
    .update({"age": 13, "favorites.color": "Red"});

배열 요소 업데이트

문서에 배열 필드가 포함되어 있으면 arrayUnion()arrayRemove()를 사용해 요소를 추가하거나 삭제할 수 있다. arrayUnion()은 배열에 없는 요소만 추가하고, arrayRemove()는 제공된 각 요소의 모든 인스턴스를 삭제한다.

final washingtonRef = db.collection("cities").doc("DC");

// Atomically add a new region to the "regions" array field.
washingtonRef.update({
  "regions": FieldValue.arrayUnion(["greater_virginia"]),
});

// Atomically remove a region from the "regions" array field.
washingtonRef.update({
  "regions": FieldValue.arrayRemove(["east_coast"]),
});

👀 Reference

profile
Flutter 개발자가 되어보자!

0개의 댓글