Firebase Firestore Database 사용해 보기 1편

Firebase Firestore Documentation

firebase_core | Flutter Package
cloud_firestore | Flutter Package

Firebase 세팅하기 - Flutter 3.0 이후
Firebase 세팅하기 - Flutter 3.0 이전

이번 글부터 Firestore에 대해서 살펴볼 예정이다.

Firestore에 대한 자세한 기능 설명은 아래에서 다루게 될 예정인데, 간단하게 Firestore가 무엇인지에 대해서 알아보자.

Firestore는 기존 Firebase의 대표적인 저장소였던 Realtime Database의 단점을 보완하여 서비스한 NoSQL 데이터베이스이다.

Firestore는 현재 Firebase의 대표적인 데이터베이스로 널리 사용되고 있고, 구글에서도 Firestore 사용을 적극적으로 권장하고 있는 추세다.
저는 3개 정도의 프로덕트를 Firestore를 사용한 배포 경험을 가지고 있어 비교적 어렵지 않게 사용하고 있는 편입니다. Firestore는 무료 사용량이 있어 초기 사용에 대한 부담이 적고, 강력한 쿼리 기능과 커서 기반 페이지네이션을 지원하며, 보안규칙을 사용한 보안 이슈도 가볍게 해결해 주고 있다. 물론 무료 사용량이 있지만 프로덕트 타입, 데이터 관리 등의 여러가지 부분을 고려해서 사용하여야 리소스를 줄일 수가 있다.

Firestore는 CRUD 건당 사용량이 체크되므로, 적은 용량의 잦은 호출이 있는 경우에 Firestore 보다는 Realtime을 사용하는 것을 추천드린다.

Realtime을 사용하신 분들은 json 형태로 관리되는 구조와는 다르게 Firestore는 Collection-Document 구조가 조금은 이해하기 힘들 수는 있지만 Field는 json 구조를 갖추고 있다.

Firestore의 가장 중요한 Collection-Document에 대해서는 실제 사용을 해보면서 더 자세히 다뤄볼 예정이다.

Firestore를 사용하기 위해서는 반드시 Firebase 세팅이 먼저 되어야 하기에 위에 공유한 Firebase 세팅을 먼저 진행하고, Fiestore를 사용하시면 된다.

여기서는 Firestore 사용을 위해 Firebase 대시보드에서 저장소를 생성하고, 대시보드 사용 방법에 대해서 다뤄볼 예정이고, Flutter에서 Fiestore에 어떻게 데이터를 저장하고 읽어오는지에 대해서 다양한 방법으로 사용해 보면서 2편으로 나눠서 진행할 예정이다.

Firebase

먼저 Firebase에서 Firestore 기능을 사용하기 위해 대시보드 설정을 먼저 진행하도록 하자.

좌측 탭에서 Firestore Database를 클릭하면 아래와 같은 화면이 나온다.

우선 데이터베이스를 만들어야 하기에 데이터베이스 만들기를 클릭하여 생성을 하도록하자.

프로덕션 모드와 테스트 모드가 있는데, 테스트 모드에서 시작을 선택하도록 하자.

만일 실제 배포할 앱이라면 프로덕션 모드로 변경해야 하는데, 이 부분은 나중에 수정할 수 있다.

추가로 프로덕션 모드에서는 보안규칙을 작성하여야 하는데, 보안 규칙에 사용하는 룰의 기반이 되는 값으로 이전에 다뤘던 Authentication에서 생성한 UID 값을 기반으로 룰을 작성한다.

보안규칙은 각 플랫폼에 맞는 규칙을 찾아서 작성하여야 하기에, 지금은 다루지 않겠다.

Firestore 저장소가 실제로 저장되는 서버의 resion을 선택하여야 한다. 드롭다운 메뉴 박스를 클릭하면 다양한 지역이 나오고 원하는 지역을 선택하면 되는데, 구글에 찾아보면 resion 위치가 있는 지도를 쉽게 찾아볼 수 있다.

resion에 따라 속도 및 금액 차이가 있어서 비교해 보면 좋긴 하지만, 저는 주로 북미 resion을 사용해 왔다.

Firestore를 사용하여 프로덕션 배포 앱 개발에 참여한 적이 있는데, 아무래도 Global을 타겟으로 하다보니 북미 resion을 선택하였다.

이 부분은 원하는 resion으로 선택하셔도 됩니다.

사용 설정을 하고나면 이제 우리의 실제 데이터베이스를 구글이 생성하고 있는 것이다. 조금만 기다리면 금방 데이터베이스가 생성이 될 것이다.

생성이 완료되었다 ! 나만을 위한 데이터베이스가 생성이 된 것이다.

DB 구조가 익숙하지 않으신 분들도 있거나 DB에 대해서 잘 모르시는 분들도 있을 것이다.

Firestore는 위에서도 설명했듯이 관계형 데이터베이스인 RDBMS가 아닌 NoSQL을 지원한다.
Collection -> Document -> Collection .. 이런 형식으로 이뤄진 구조의 데이터 베이스인데, 익숙하지 않으신 분들을 위해서 한 번 DB를 생성해 보도록 하겠다.

컬렉션 시작을 클릭해보자. 아래 이미지와 같은 창이 하나 열린다. 컬렉션 ID를 넣으라고 하는데, "test"라고 넣어주고 다음을 클릭해보자.

자 이제 컬렉션을 생성했으니 Document에 해당되는 부분을 만들어 주면된다.

Document ID는 자동 ID를 선택해서 자동 생성해주고, 아래 이미지와 같이 아무렇게나 필드 / 타입 / 값을 넣어보고 저장을 해보자.

Firestore DB에 실제로 저장되는 데이터 필드는 key-value 형태이며, Flutter에서 Map 구조와 동일하다.

Firestore DB에 방금 입력한 데이터가 실제로 저장이 되었다. 그런데 여기서 보면 필드 위에 컬렉션 시작 버튼이 안에 생성이 되어있다.

컬렉션 시작을 다시 눌러보자.

이번에는 컬렉션 ID를 test_test로 만들고, Document ID는 아까전과 동일하게 자동 ID로 만들어주고 필드 값을 아무렇게나 넣어보자.

위에서 생성한 Collection -> Document 안에 다시 Collection -> Document가 생성이 되었다.

익숙하지 않더라도 Firestore를 사용하려면 익숙해지긴 해야 합니다..

상단의 Document ID를 클릭하면 최상단의 컬렉션으로 나올 수 있는데, 이동 후에 아래 이미지와 같이 컬렉션을 삭제해보자.

Collection ID를 넣어주면 삭제를 할 수 있다. 이제 DB에 아무것도 저장되있지 않은 상태로 되었다.

간단하게 Firestore DB에 대해서 살펴 보았는데, 아직 이해가 잘 안되실 수도 있다. 당연히 익숙하지 않은 DB 구조거나 또는 DB에 대해서 잘 모르시면 그럴 수 있다.

Firestore를 Flutter를 통해서 사용해 보면서 익숙해지도록 하자.

Firestore 대시보드 상단 탭의 규칙을 눌러보자. 여기 규칙이 위에서 설명한 보안규칙을 작성하는 부분이다.

테스트 모드로 생성했기에, 한달 간만 사용할 수 있는데, 보안규칙 아래에 보면 만료 일자가 나와있다. 이 부분의 연도를 2024년으로 설정하면 내년까지 계속 사용할 수 있다.

사용량 탭을 눌러보자. Firestore 사용량에 대해서 나와있는데, 위에 Firestore 읽기 / 쓰기 / 삭제 건수에 대한 사용량이고, 아래 구독 측정항목은 스냅샷 리스너 연결에 대한 사용량이다.

Firestore는 조회 건당 비용이 발생하는데, 이 부분을 잘 고려해서 앱을 설계해야 비용을 아끼면서 앱을 운영할 수 있다. 하루 단위로 업데이트 되며, 하루 기본 무료 사용량이 있으니 테스트 중에는 안심하고 사용해도 된다.

스냅샷 리스너에 대한 부분은 Flutter에서 사용하면서 추가적으로 알아보도록 하겠다.

Flutter

가볍게 Firestore DB 구조 및 대시보드 사용 방법에 대해서 살펴보았다. 이제 Flutter를 통해서 데이터를 저장, 삭제, 변경해 보고 쿼리 조회를 하면서 대시보드의 변경을 확인해 보자. 우선 코드 따라서 작성하면서 사용해보면 어렵지 않게 자유롭게 Firestore를 사용해 볼 수 있다.

기능 하나씩 차근차근 살펴보도록 하자.

만일 아직 Firebase 세팅이 되어있지 않다면 위에 공유한 링크 중 각 버전에 맞는 설정 방법을 보시고 설정부터 우선하셔야 합니다.

dependencies

dependencies:
	cloud_firestore: ^4.4.3
    firebase_core: ^2.7.0

CREATE

Firestore DB에 먼저 데이터를 저장해보도록 하자. 데이터를 저장할 때 대시보드에서 했던 것처럼 collection ID를 지정해 주어야 한다.

그 다음에 Document ID를 생성하고 필드에 데이터를 저장하여야 하는데, Document ID 생성 방법이 자동 생성과 수동 생성 두 가지 방법으로 나뉜다.

각각 살펴보자. 먼저 Document ID를 우리가 원하는 값으로 생성해보자. "cars"라는 컬렉션 ID를 생성하고 document ID는 아무렇게나 생성하였다. set에 데이터를 입력해주면 저장이된다.

데이터 필드에 저장될 구조는 key-value 구조로 flutter에서 Map 형태랑 같은 구조라고 생각하면 된다.

우선 처음해보는 것이니깐 객체로 생성해서 예제를 만들지는 않았다.

FirebaseFirestore _firestore = FirebaseFirestore.instance;
await _firestore.collection("cars").doc("123456789").set(
                {
                  "brand": "Genesis",
                  "name": "G70",
                  "price": 5000,
                },

이번에는 doc 부분의 값을 넣지 않고 자동으로 Document ID를 생성하도록 하였다.

FirebaseFirestore _firestore = FirebaseFirestore.instance;
await _firestore.collection("cars").doc().set(
                {
                  "brand": "Genesis",
                  "name": "G80",
                  "price": 7000,
                },
              );

Firestore 대시보드에서 데이터를 확인해 보자. 자동으로 ID가 생성된 것을 확인할 수 있다.

두 번 데이터를 저장 하였는데, 컬렉션은 하나만 생성이 되었다. Collection, Document는 중복 생성이 안된다. 동일한 ID가 있으면 해당하는 ID를 찾아서 필드에 데이터를 추가로 저장시키는 방식이다.

그렇다면 이번에는 위에서 사용한 document ID를 똑같이 주어서 다른 데이터를 넣게 되면 어떻게 될까 ?

FirebaseFirestore _firestore = FirebaseFirestore.instance;
await _firestore.collection("cars").doc("123456789").set(
                {
                  "brand": "Genesis",
                  "name": "G90",
                  "price": 12000,
                },
              );

데이터가 변경된 것을 확인할 수 있다. 기존 데이터가 컬렉션, 도큐먼트 ID가 동일하기에 데이터 필드를 덮어씌운 것이다. 이 부분은 아래에 데이터를 Update하는 부분에서 계속해서 다뤄보도록 하겠다.

이번에는 Document안에 다시 Collection을 넣어서 데이터를 저장해보자.

cars 컬렉션안에 options 컬렉션을 생성하고 데이터를 저장하자.

 FirebaseFirestore _firestore = FirebaseFirestore.instance;
              await _firestore
                  .collection("cars")
                  .doc("123456789")
                  .collection("options")
                  .doc()
                  .set({
                "navigation": true,
                "color": "black",
              });

아래와 같이 데이터가 컬렉션 안에 생성된 것을 확인할 수 있다. 이렇게 데이터 간의 연결성이 필요하거나 서브 데이터의 저장이 필요한 경우 사용하면 된다.

만약에 필드 데이터를 하나의 key-value만 추가하고 싶다면 어떻게 해야될까 ? 이 부분은 Update 기능에 대해서 알아보면서 살펴보도록 하자.

UPDATE

이번에는 저장한 데이터를 변경하는 Update 기능에 대해서 살펴보도록 하자.

저장된 Firestore DB에서 데이터를 수정하려면 어떻게 해야 될까 ? 방법은 크게 두 가지 방법이 있다.

첫 번째는 데이터 필드의 key-value만 수정하거나, 추가하는 방법이 있고, 또 다른 하나의 방법은 데이터 필드를 읽어온 다음 읽어온 데이터 구조의 key-value 값을 추가하여 덮어씌우는 방식이 있다.

우선 우리는 DB를 읽어오는 기능에 대해서는 배워보지 않았으니 코드만 간단하게 살펴보도록 하자. Read 기능에 대해서 살펴볼 때 더 자세히 다룰 것이니 여기서는 가볍게만 보면 된다.

먼저 데이터를 읽어서 덮어씌우는 방식으로 update를 진행해 보자.

"cars" 컬렉션에서 "123456789" 도큐멘트에 저장되어 있는 데이터를 가져온 뒤, price의 값만 변경해 주어서 다시 저장을 하였다.

FirebaseFirestore _firestore = FirebaseFirestore.instance;
DocumentSnapshot<Map<String, dynamic>> _data = await _firestore.collection("cars").doc("123456789").get();
              Map<String, dynamic> _newData = {
                "brand": _data.data()!["brand"],
                "name": _data.data()!["name"],
                "price": 15000,
              };
await _firestore
                  .collection("cars")
                  .doc("123456789")
                  .update(_newData);

Firestore에도 데이터가 변경되서 저장된 것을 확인할 수 있다. 뭔가 비효율적인 것처럼 보이지만 덮어씌워야 하는 경우도 있으니, 잘 기억해 두자.

이번에는 변경하고 싶은 key의 value만 변경하는 방법을 알아보자.

해당하는 컬렉션 > 도큐멘트의 key-value를 업데이트 해주면 변경이 된다.
데이터를 읽어와서 변경하는 것보다 상대적으로 가볍긴 하지만, 사용하다 보면 이렇게 업데이트가 힘든 경우가 발생할 수도 있다.

FirebaseFirestore _firestore = FirebaseFirestore.instance;

await _firestore.collection("cars").doc("123456789").update({
                "price": 10000,
              });

데이터 업데이트가 성공적으로 되었다.

이번에는 Create 기능에서 다루지 못했던 데이터를 추가하는 방법에 대해서도 살펴보자. 데이터를 추가한다는 것은 결국 필드 데이터가 변경되는 것이기에 create가 아닌 update 기능을 사용하여야 한다.

데이터를 추가하는 방법도 수정하는 것과 같이 데이터를 읽어와서 읽어온 데이터에 새로운 데이터를 추가하여 덮어씌울 수도 있고, 단순히 key-value만 넣어줘도 된다.

데이터를 읽어와 trim을 추가해 보자.

FirebaseFirestore _firestore = FirebaseFirestore.instance;
DocumentSnapshot<Map<String, dynamic>> _data = await _firestore.collection("cars").doc("123456789").get();
Map<String, dynamic> _newData = {
	"brand": _data.data()!["brand"],
	"name": _data.data()!["name"],
	"price": _data.data()!["price"],
	"trim": "3.5 AWD",
	};
await _firestore.collection("cars").doc("123456789").set(_newData);

잘 적용이 되었다.

이번에는 key-value만 추가해보도록 하자.

FirebaseFirestore _firestore = FirebaseFirestore.instance;

await _firestore.collection("cars").doc("123456789").update({
                "fuel": "Gasoline",
              });

"fuel" key가 추가된 것을 확인할 수 있다.

여기까지 해서 Collection, Document, Field를 생성하는 CREATE 기능과 저장된 Field 데이터를 수정 / 추가하는 UPDATE 기능에 대해서 간단하게 살펴보았다.

생성하고 수정하고 추가를 해봤으니 삭제하는 방법에 대해서도 알아야 한다.

DELETE

삭제하는 방법에 대해서 살펴보자. 삭제는 필드안에 특정 key-value만 삭제를 할 수도 있고 document 하나를 삭제할 수도 있다.

먼저 데이터 필드의 특정 key-value만 삭제하도록 하자. 삭제 방법은 update 기능을 사용하여 삭제하고자 하는 key 값을 넣고 value 값으로 FieldValue.delete()를 넣어주면 된다.

FieldValue에 대해서는 추후에 Firestore를 활용하는 방법에 대해서 소개할 때에 다루게 될 것이다.

FirebaseFirestore _firestore = FirebaseFirestore.instance;

await _firestore.collection("cars").doc("123456789").update({
                "fuel": FieldValue.delete(),
              });

"fuel" key 데이터만 삭제된 것을 확인할 수 있다.

이번에는 데이터를 읽어와 "trim" 데이터를 제거하고 덮어씌우는 방식으로 삭제를 해보자.

FirebaseFirestore _firestore = FirebaseFirestore.instance;
DocumentSnapshot<Map<String, dynamic>> _data = await _firestore.collection("cars").doc("123456789").get();
	Map<String, dynamic> _newData = {
		"brand": _data.data()!["brand"],
		"name": _data.data()!["name"],
		"price": _data.data()!["price"],
		};
await _firestore.collection("cars").doc("123456789").set(_newData);

정상적으로 삭제가 되었다.

이번에는 Document를 삭제하도록 하자. 간단하다.

Document ID로 삭제하면 즉시 삭제가 된다. 여기서 주의할 점이 하나있다.

 FirebaseFirestore _firestore = FirebaseFirestore.instance;
              await _firestore.collection("cars").doc("123456789").delete();

삭제된 데이터를 보면 필드 데이터는 삭제가 되었는데, 서브 컬렉션은 아직 남아있는 것을 확인할 수 있다.

서브 컬렉션 까지 삭제를 하고 싶다면 코드를 추가해 서브 컬렉션을 전부 지우고 도큐멘트를 삭제해야 말끔히 삭제시킬 수 있다.

컬렉션을 삭제하는 방법에 대해서 알아보자.

우선 간단하게 test라는 컬렉션 하나를 만들어보자.

"test" 컬렉션을 가지고와 반복문을 통해 해당하는 DocumentSnapshot을 찾아 삭제를 하여야 한다.

잘못된 컬렉션을 삭제하면 안되니 유의해서 사용하여야 한다.

FirebaseFirestore _firestore = FirebaseFirestore.instance;
          
await _firestore.collection("test").get().then((snapshot) {
                for (DocumentSnapshot ds in snapshot.docs) {
                  ds.reference.delete();
                }
              });


사용량

지금까지 Firestore에서 발생한 사용량을 확인해보자. 읽기에 대해서는 8만을 무료로 제공하고, 쓰기/삭제는 2만을 지원해주는 것을 확인할 수 있다.

적으면 적고 많다면 많을 수 있는데, 개발시에는 전혀 부족함이 없지만 저는 DAU 3000명이 활동하는 SNS 서비스에 Firestore를 DB로 사용해본 적이 있는데, 정확한 금액은 확인해야 겠지만 보통 하루 평균 읽기가 20만 정도 사용했던 것 같다.
반면 쓰기와 삭제는 2만을 잘 안넘겼다..

사용량도 체크해 보면서 리소스를 줄일 수 있는 로직을 개발하는 것도 재미있는 경험이 될것이다. 자주 체크해보시길 바란다.

마무리

Firebase Firestore를 세팅하고 대시보드 기능을 확인해봤다. 그리고 대시보드에서 직접 Collection과 Document를 생성하여 데이터 필드를 추가하면서 No-SQL 데이터베이스 구조에 대해서도 살펴보았다.

이어서 FLutter에서 Firestore DB를 다루는 방법에 대해서 알아보았는데, CRUD 중 CREATE, UPDATE, DELECT에 대해서만 다뤄보았다.

READ 기능은 내용이 너무 많고 기능도 다양해서 다음 편에서 다루게 될 예정이다.
단순히 DB를 읽어서 가져오는게 전부가 아닌 Query를 통해 조회하는 기능도 있어서 배워야 할 내용이 가장 많은 기능이다.

계속해서 다음편에 READ 기능에 대해서 살펴보도록 하자.

profile
Flutter Developer

0개의 댓글