본문 바로가기
코딩생초보를 위한 플러터 빠르게 한바퀴

7.firebase1-firestore

by 타이싸란 2023. 9. 2.

7회차 세부 과정 목차

 

 

숙제를 통해 외부통신을 이용하여 데이터 받아오기를 연습해봤습니다.

우리가 사용한 api 는 오픈api 로 모두에게 공개되어있는건데요,

이런것들이 post 가 되는지는 모르겠지만, 결국 앱을 만들기위해서는

crud 가 되어야합니다.

c : create 생성

r : read 읽기

u : update 수정

d : delete 삭제

 

우리가 숙제에서 한건 read 밖에 없어서 좀 한계가 있었습니다. 그리고 데이터베이스도 만져볼수가 없죠.

 

우리가 백엔드 서버가 따로 있다면 이 모든것들을 다할수있고,

post 방식을 이용하면서 body 에 값넣어서 호출하고, 로그인도 해보고 등등

정석적으로 할수있는데, 지금은 front 의 영역인 플러터를 배우고 있는상황에서 백엔드를 구축한다는건 또 다른영역을 배워야하는거라 할수가없습니다.

그래서 서버리스 서비스를 사용할건데요, 서버리스란 진짜로 서버가 없다는 뜻이 아니고 , 프론트개발자가 백엔드서버를 따로 구축하지 않아도 어떤 백엔드 비슷무리한걸 이용할수있는 그런 서비스입니다.

 

몇개있는데 대표적으로 구글에서 제공하는 firebase 가 있죠.  사실 전 firebase 밖에 안써봤습니다.

firebase에는 여러가지 기능들이 있는데 가장 대표적으로 이용할수있는게 파일서버랑(firestorage) , 데이터베이스(firestore), 계정(authentication) 입니다.

 

여기 데이터베이스는 noSQL 방식이고 , 리스트 카운트 하는거에도 약간 별로고 , 나중에 호출많이 일어나면 돈이 많이들고 하는 상황이 있는데요, 우선 백엔드가 없는 상황에서 빠르게 만들어보는거에는 이것만한게 없는거 같습니다.

 

그래서 많은 강사님들도 firebase 로 수업을 하곤하죠.

그리고 왠만한 모바일서비스 특화된 스타트업들은 이것만해도 충분할것 같다는 생각도듭니다.

 

7,8, 강은 firebase에 대해서 할건데

7강 - firestore

8강 - authentication -> 줄여서 걍 auth 라고하겠음. , storage

 

요렇게 할겁니다.

어쨌든 한번 해봅시다.

 

 

1. firebase 기초세팅

목차로돌아가기

 

먼저 구글계정이 있어야하는데 , 없으면 하나 만드시길바랍니다.

그리고 firebase 로 와주세요.

https://firebase.google.com/

 

Firebase | Google’s Mobile and Web App Development Platform

Discover Firebase, Google’s mobile and web app development platform that helps developers build apps and games that users will love.

firebase.google.com

 

콘솔로 이동 , 또는 시작하기를 눌러주세요.

 

처음 하시는거라면 기존의 프로젝트 만든게 없어서 아무것도 안뜨실거에요.

프로젝트 만들기를 눌러주세요.

 

1. 프로젝트 이름 지정하세요. 이건 flutter의 패키지이름과는 상관없고, 자신이 알아보기쉬운걸로 지으면됩니다

2.에널리틱스 할지 안할지 선택인데 해도되고 안해도됩니다. 그냥 한번해볼게요

3.에널리틱스 설정에 체크할거하고 해주세요.

4.프로젝트가 생성됩니다. 조금만 기다려주세요

 

 

생성이 되면 이 firebase 프로젝트와 연결할 어플리케이션을 등록해야합니다.

 

firebase를 사용하기위해서는 기초설정 + 맞춤설정 을 해줘야하는데요,

 

기초설정은 firebase를 사용하기위한 프로젝트 세팅이고 ,

맞춤설정은 firebase의 여러가지 서비스들이있는데 각 프로젝트에서 사용하는 서비스에 대해 라이브러리 세팅등을 하는겁니다.

 

맞춤설정은 각 서비스 사용할때 해보도록합시다. 우선 기초설정부분에서는

ios 와 안드로이드를 등록하면되는데,

예전에는 저 버튼을 하나하나 눌러서 등록해주고 , 플랫폼별 내부 설정도 해주고 했었어야 했는데,

기초 설정 부분이 워낙에 간단한데 비해 해줄게 많았습니다.

(google.service.json 다운받아서 안드로이드 ios에 설정해주기, 안드로이드 gradle설정해주기, ios 세팅 설정해주기 등등)

 

그런데 flutter가 요즘 많이 쓰기도하고 , 구글에서 만든거기도하고 flutter + firebase 조합으로 많이하다보니

flutter 사용자가 firebase cli 라는걸이용해서 기초설정을 자동으로 해주는게 생겨버렸습니다.

(그래도 처음엔 설치하고 뭐하고 할게 조금있긴합니다)

 

그래서 맨 뒤쪽에 있는 flutter 로고버튼을 한번 눌러보겠습니다.

 

그러면 버튼하나 누른다고 해서 설정이되는게 아니고 , flutter 에서 간편하게 사용하려면 이러이러한 설정을 해주면 된다~ 라는 설명이 나옵니다.

그래서 몇가지 설정이 있는데 나열해보면

 

1.firebase cli 설치

2.flutter sdk설치 -> 이미 되있음

3.firebase cli 명령어를 어디에서 치든 작동할수있도록 global로 등록하고 firebase로그인하기

4.우리 flutter 프로젝트에 명령어로 연동하기

5.에러나면 설정 좀 조율하기

6.flutter 라이브러리 firebase core 세팅

 

이정도 입니다.

1번부터 해보죠.

1.firebase cli 설치

Firebase CLI 설치 및 로그인이라고 친절히 링크 걸어준거 눌러가보면 

이런화면이 나오는데 , 자신의 개발환경에 맞는걸 선택해줍니다. 저는 mac이에요.

그리고 어떻게 설치할지 나오는데 저는그냥 명령어로 설치하렵니다

 

curl -sL https://firebase.tools | bash

 

요거를 터미널 열어서 쳐주세요. 자신이 bash 가 아니라 zsh를 쓴다고 해도 그냥 위에 명령어를 쳐주세요.

 

1). 터미널 열어서 위 명령어를 친 화면입니다.

2). 그리고나서 .zshrc 파일에 export PATH=$PATH:$HOME/.pub-cache/bin 이거를 추가해주세요.

(처음에 flutter sdk 경로적어줬던 파일이 .zshrc 입니다. 설치강의때 제가 이거 어디저장했는지 잘기억하라고했었죠)

그러면 firebase cli 설치가 되었습니다.

 

 

그럼 이제 2.flutter sdk설치 -> 이건 이미 설치되었으니 3번을봅시다.

3.firebase cli 명령어를 어디에서 치든 작동할수있도록 global로 등록하고 firebase로그인하기

 

 

 

그냥 터미널에다가

dart pub global activate flutterfire_cli  => 이거 쳐주면됩니다.

설치했으면 flutterfire --version 으로 한번 확인해보시길 바랍니다.

 

firebase login을 쳐서 로그인해주세요.

y, n 선택하는게 나오는데 y 해주면

계정선택창이 나타납니다. 선택해주세요.

 

허용 눌러주시면, login successful 안내가 나타납니다.

터미널에서도 로그인 성공했다고 표시됩니다.

 

4.우리 flutter 프로젝트에 명령어로 연동하기

 

우리 flutter 프로젝트랑 연동할건데 저기 빨간박스부분을 복사해줍니다.

 

그다음 우리 프로젝트로 와서 터미널을 열어주세요.

그리고 복사한 명령어를 쳐줍니다.

(명령어칠때 경로가 맞는지 확인하세요. 근데 자동으로 설정되어있을거에요. 저같은경우 명령어 칠때 앞에 경로가 basic_flutter_7이라고되어있죠)

 

그러면 어떤 프로젝트를 등록할거냐 나오는데 안드로이드랑 , ios 를 등록해야되는데 기본적으로 두개 체크가 되어있습니다.

그래서 그냥 엔터를 쳐주세요.

 

그러면 y,n 묻는게 나오는데 y 로 입력하고 엔터치시면

아래처럼 잘 세팅됬다고 나옵니다.

프로젝트를 보시면 몇가지 파일이 생긴걸 볼수있습니다.

그리고 안드로이드의 gradle설정과 ios의 세팅부분이 자동으로 들어가게됩니다.

 

(사실 CLI가 편하긴 한데 , 처음엔 매뉴얼 으로 해보면서 한번 귀찮아봐야 뭐가 어떤게 자동으로 되는지 아는데, flutter firebase 연동 인터넷에 치면 많이 나오니까 매뉴얼로 세팅하는것도 한번 해보시길 바랍니다.)

 

5.에러나면 설정 좀 조율하기

앱을 실행시켜봐서 잘나오면 다행인데, gradle 버전 에러가 나는 부분이 있을수 있습니다.

그부분만 좀 고쳐보겠습니다.

 

안드로이드 부분에는 build.gradle 이라는 파일이 2개가 있습니다.

1. 프로젝트 수준의 build.gradle => google-services:4.3.10 으로 되어있는걸 4.3.13으로 고쳤습니다.

2. 앱수준의 build.gradle => minSdkVersion 을 22로 수정 , (complieSdkVersion, targetSdkVersion 33으로 수정 => 미필수)

 

저는 이렇게 하니 버전문제는 사라지고 앱이 잘 실행되었습니다.

 

6.flutter 라이브러리 firebase core 세팅

 

이건 CLI 와 상관없이 매뉴얼로해도 해줘야 되는 부분입니다.

 

 

flutter 라이브러리 다운받는곳에 firebase_core를 쳐서 버전 확인한다음 pubspec.yaml에 라이브러리를 등록해주세요.

그리고 main 함수쪽에 와서 runApp메소드 전에 Firebase.initializeApp 을 해줘야합니다. WidgetFlutterBinding도 해줘야합니다. 위처럼 하면됩니다.

 

이렇게 하면 기초세팅이 끝났습니다.

처음 세팅하는거라 생각보다 좀 길었는데 , 1,2,3번 은 이미 설정되어있으니 다음 프로젝트 열었을때는 생략해도 되는부분이라 , 다음번부터는 좀더 쉽게할수있습니다.

 

2.FireStore 단점

목차로돌아가기

 

파이어스토어는 파이어베이스에서 제공하는 데이터 베이스입니다.

 

리얼타임데이터베이스

파이어스토어

 

이 두개를 제공하는데 리얼타임데이터베이스는 옛날꺼고 더이상 업뎃은 안하는거 같습니다.

지금은 파이어스토어를 사용하면됩니다. 

파이어스토어는 noSQL 방식입니다. SQL 이 아니라는거죠.

일반적인 데이터베이스는 관계형데이터베이스로 SQL 언어? 를 사용해서 CRUD 를 하는데 이건좀 다릅니다.

몽고DB 랑 비슷하다고 보면됩니다.

 

공부하기전 파이어스토어의 단점과 한계부터 말씀드리고자하는데요, 시작도안한시점에서 이해는 안되지만 일단 언급해봅니다.

 

파이어스토어를 쓰면서 최대단점이라 생각하는 3가지가

1.데이터베이스 자체에서 JOIN 이라는게 안되서 데이터를 몇번 불러와서 조합을 해야한다는 것

2.카운팅이 안되는것

3.다중조건이 안되는것(인덱스생성으로 극복)

 

<JOIN>

예를들어 하나의 게시글이 있고 아래에 10개의 댓글이 달려있으며 댓글머릿말에 작성자의 썸네일과 닉네임을 표시하려고 하면

1.일단 게시글 하나를 불러옴

2.게시글에 달려있는 10개의 댓글을 불러옴( 여기까진 괜찮음 )

3.댓글에는 댓글작성자의 정보가 다 담기는게 아니라 유저를 특정할수 있는 key만 저장함

4.각댓글마다 작성자key를 불러와서 작성자정보10개(댓글이10개니까) 를 불러와서 거기의 작성자의 닉네임과 썸네일을 불러옴

5.댓글ui에 작성자의 닉네임과 썸네일을 표시함.

 

이렇게 되는데 그러면 게시글 하나를 화면에 표시하기위해 1개+10개+10개 이렇게 총 21개의 문서를 부르게되는것입니다.

파이어스토어의 요금없는 기본 호출건수가 하루에 5만건이니, 조금만 코딩을 잘못짜거나(페이징이 없다던지) 다른정보를 조합해야하는 상황이 많다면 요금이 많이 나올수 있고 , 속도도 느려질수있는 단점이 있습니다. 

 

3번에서 댓글문서에 작성자의 key만 저장하는게 아니라 닉네임과 썸네일도 같이 저장하면 안되냐했을때, 해도되지만 유저가 닉네임이나 썸네일을 수정하였다면, 예를들어 10월1일에 댓글을 남기고 , 10월 5일날 닉네임과 썸네일을 바꿨다 친다면 이미 작성된 댓글에는 최신의 닉네임과 썸네일이 반영되어 있지 않는 문제가 있습니다. 그렇다고 유저가 닉네임과 썸네일을 바꿨을때 , 유저가 남긴 댓글을 모두추적하여 수정하는것도 비용이 들고, 불합리 하죠.

 

참고로 관계형 DB에서는 JOIN을 통해 한번의 쿼리호출로 결과값을 받아낼수있습니다.  

 

<카운팅>

자유게시판에 1030개의 글이 올라와있다고 칩시다.

게시판리스트 보여주는 상단에 총 게시글갯수를 보여주고자합니다.

아래에는 1,2,3,4 페이지를 보여주고 이동할수있는 페이지 이동버튼이 있다고합니다.

페이지당 50개의 리스트를 보여줍니다.

 

1.페이지가 몇페이지 까지 있는지 알기위해서는 (총 게시물수 , 한페이지당 보여줄 갯수) 이 두가지가 필요함.

2.1030개글이 있고 50개씩보여주니까 페이지는 21페이지 까지 있음.

3.1030개라는 글이 있다는것을 알아내기위해서 게시글을 모두조회함(1030개 문서호출발생)

 

단순히 자유게시판에 들어와서 사용자가 게시글을 보지도 않았는데 페이징화 보여주기위해 1030번이나 문서호출이 발생하였습니다.

아주 불합리하죠.

그러면 따로 자유게시판 카운팅이라는 문서를 만들어서 게시글 1개올라갈때마다 카운팅 1증가시켜서 그걸부르면 1번만 부를수있지 않느냐?할수있는데 , 그건 전체글에 대한 카운팅을 할때는 유효할수있는데 , 조건검색이 들어가게되면 또 안되는거죠.

 

그래서 파이어스토어를 데이터베이스로 하는 앱들을 보면 무한스크롤 형태 + 페이징을 이용하여 데이터를 보여줍니다. 페이지가 몇페이지냐 총 몇개의 글이있냐 그런거는 제공을 안해주죠. 그저 아래로 계속내리면 데이터 더이상없을때까지 10개나 20개씩 계속 뿌려주게 되있습니다.

 

<다중조건>

자유게시글을 불러오는데

isDeleted가 false , (논리삭제 안된것)

writeUid = "asdfasdf" , (특정유저지정)

likeCounting >=100 , (좋아요 100개 이상되는 게시글)

createAt기준으로 최신거 순서대로 불러옴

이 4가지의 조건을 줘서 데이터를 불러오고자함

 

이렇게 했을때 where 조건이 2개까지는 불러와지는데 3개이상이 되거나 , orderby가 합쳐지면 에러나면서 못불러온다고 나옵니다.

이럴때는 isDeleted , writeUid , likeCounting , createAt 이 4가지 필드로 index를 만들어 조회해야합니다.

어쨋든 되니까 상관없다고 볼수있지만, 우리가 딱 저 특정조건만 검색하는게 아니라 5개조합도 할수있고 조합의 구성도 바꿀수있는데

그때마다 거기에 맞는 index를 생성해야한다는 것입니다. 그렇기 때문에 굉장히 성가시죠. 인덱스 만드는 갯수도 무료한계가 있습니다.

 

어쨌든 이 3가지의 큰 첼린지가 있다는것을 염두해두고 앱을 만들어야 겠습니다.

 

 

3. firestore 콘솔 사용하기

목차로돌아가기

 

firebase cli 세팅이 끝났으니 firestore을 한번 사용해봅시다.

 

firebase 콘솔에서 Firestore Database를 찾아서 데이터베이스 만들기를 선택해줍니다.

 

프로덕션 모드 , 테스트모드 고를수있는데,

이건 그냥 규칙란 초기세팅이 변경되는거 말고는 차이점이 하나도없습니다.

 

테스트모드로 하면 , 모든 사용자사 읽기,쓰기 가능한대 몇월 몇일까지만 가능하다~ 라는걸로 초기세팅되고

프로던셕모드에서는 firebase auth라는 인증을 받은사람이 아니면 읽고쓰기가 안된다~ 라는걸로 초기세팅되어있습니다.

 

말그대로 초기세팅이고, 변경하는 되는문제니까, 그냥 프로덕션 모드에서 시작 으로 선택해주세요.

 

파이어스토어 데이터베이스가 물리적으로 저장될 장소를 선택하는것입니다.

 

어쨋든 데이터가 하드디스크나 SSD에 저장되어야 될것이니까, 그 저장소가 데이터 센터에 차곡차곡 들어있는데

그 데이터 센터 위치를 어디로 할까 ? 입니다. 

서울에 있는 데이터센터를 선택해줍시다. 왠지 한국에 있으니까 한국에서 사용하면 더빠를것 같은 기분이 드니까요.

그리고 지금 선택한 데이터 센터는 해당프로젝트가 없어질때까지 바꿀수가 없고 , 파일같은거 저장하는 데이터센터 위치와 동일합니다.

 

즉 파이어스토어(데이터베이스 서버) 는 서울로하고 , 스토리지(파일서버) 는 미국으로 할수없다는 것입니다.

 

시간이 몇초정도 지나면 firestore가 만들어집니다.

먼저 규칙 탭으로 와서 위에처럼 고쳐주세요. 그냥 : if false; 이부분 지워주면됩니다.

 

그러면 아무나 접근해서 읽고 쓰기 가능한 데이터베이스다~ 라고 규칙을 만들어 두는겁니다.

물론 나중에 실제 서비스할때는 규칙을 좀 꼼꼼하게 적어야겠죠.

특정 컬렉션은 특정 인간만 접속할수있다던지요. 나중에 규칙도 잘적으면 플러터상에서 적는 코드량도 줄어들고 프론트단에서 안막아도 서버단에서 막아준다던지 그런효과를 줄수있는데 그건 나중에~ 혼자서 규칙에 대해 공부한뒤 적용해보시길 바랍니다.

 

어쨋든 이렇게 규칙적은다음 꼭 게시를 눌러주세요.

 

 데이터 탭으로 와서 컬렉션을 한번 만들어봅시다.

컬렉션은 엑셀로 치면 엑셀 하단에 나오는 시트 라고 생각하시면 될것같네요.

 

저는 Post라는 컬렉션을 생성하고, 첫번째 문서를 한번 만들어 보았습니다.

(문서 ID는 직접 적어줄수도있고 자동 ID를 부여할수도 있습니다. 이건 같은컬렉션안에서 중복되지 않는 값이어야합니다.)

 

1개 정도 만들때는 손으로 만들수있는데, 10개를 만들어야한다?  그럼 10번의 노가다를 해야하죠.

그리고 title,contents 이똑같은걸 계속처가면서 한다는게 의미도 없고 현타도 나니까, 콘솔에서 직접 입력하는건 한번만 할게요.

 

어쨋든 엑셀과 비교해서 구조를 본다면, 딱정확하다고는 할수없겟지만

 

 

대충 이런 구조라고 생각하시면 되겠습니다.

 

컬렉션이 있고, -> 하부에 문서가 있다. -> 문서는 필드를 가진다.

 

(문서가 또 하부에 컬렉션을 가질수도 있는데, 이건좀 익숙해진다음 하시길..채팅데이터베이스 만들때 많이쓰긴함) 

이렇게 콘솔을 이용해서 CRUD 중에 C를 해봣습니다. ( Create 생성)

 

 

 

문서에 각필드의 오른쪽에 보면 필드의 내용을 수정할수도 있습니다.

필드 네임은 수정못하는데 필드 내용은 수정할수있습니다. 알아서 수정해보시길 바랍니다.

이렇게 콘솔을 이용해서 CRUD 중에 U를 해봣습니다. ( Update 수정)

 

위의 그림처럼 필드삭제, 문서삭제, 컬렉션 삭제를 할수있습니다.

이렇게 콘솔을 이용해서 CRUD 중에 D를 해봣습니다. ( Delete 삭제)

 

패널뷰가 아닌 쿼리 빌드를 선택해주세요.

컬렉션, 컬렉션 그룹중에 컬렉션을 선택한뒤 경로를 적어주세요. Post컬렉션이다 그러면 /Post겠죠

이렇게 실행 누르면 결과값이 나옵니다.

 

조건도 걸어서 조회할수 있습니다.

이렇게 콘솔을 이용해서 CRUD 중에 R를 해봣습니다. ( Read 읽기)

 

이렇게 콘솔을 이용해서 CRUD 모두를 해봤는데요.

이건 관리자니까 가능한방식이고 , 일반 유저들은 콘솔을 이용못하겠죠

 

다음파트에서는 클라이언트단 에서 CRUD를 해보겠습니다.

 

4. firestore  변수타입

목차로돌아가기

 

 

먼저 pubspec.yaml 을 통해 cloud_firestore 라는 라이브러리를 설치해줍니다.

기본적으로 firebase_core는 설치되있어야한다는걸 잊지마세요~

main 함수에 runApp 이 실행되기전에 위의 2개가 적혀있어야 한다는것도 잊지마세요.

 

먼저 대충빨리 결과를 보기위해서 로직을 한 페이지에 다 구성해볼게요.

 

먼저 firestore 인스턴스를 생성해야합니다.

class의 상단에 이렇게 인스턴스를 선언해줬어요.

FirebaseFirestore firestore = FirebaseFirestore.instance;

 

getData() 메소드가 실행되면 print 함수를 통해 문서갯수를 찍어보게하였습니다.

 

firestore.collection('Post') 이렇게 하면 우리가 콘솔에서 만들었던 Post 컬렉션을 불러오게되고

변수타입은 CollectionReference 가 됩니다. 대충 변수명은 post 로 했구요,

 

그 컬렉션의 문서 그룹을 불러오는게 get() 메소드입니다. post.get() 이렇게 하면 Post에 있는 모든 문서들이 호출됩니다.

조건을 넣는다하면 post.where(조건).get() 이렇게 하면 조건에 맞는 문서들만 불러와지게 되겠죠.

어쨋든 1개가들어있든 2개가들어있든 n개가 들어있는 문서그룹으로 들어오게되는데 그것을 QuerySnapshot 이라고합니다.

다시말해 QuerySnapshot은 n개의 문서가 들어있는 문서집합입니다. 그리고 그것의 변수명을 querySnapshot 이라고지었습니다.

 

querySnapshot.docs 를 print로 찍어보니 배열형태로 나오는걸 볼수있습니다.

 

첫번째 값을 확인해보고싶어서

QueryDocumentSnapshot d= querySnapshot.docs[0] 이렇게 바꾼다음 d.data()를 print 찍으보니

{} 이렇게 객체형태로 나온다는걸 볼수있습니다.

 

 

특정문서를 한개 꼭 찝어서 들고오는방식도 있습니다.

 

아까는 post.get() 이렇게했다면

이번에는 post.doc('특정문서ID').get()

이렇게 불러옵니다.

이렇게 불러온 한개의 문서는 DocumentSnapshot 이라는 변수타입을 가집니다.

아까 QuerySnapshot으로 불러온 것의 한개한개 문서는 QueryDocumentSanpshot이 었는데 이번엔 DocumentSnapshot 이네요

 

대충 그림으로 표현해보자면 이렇게 구성되어있습니다.

 

QueryDocumentSnapshot과 DocumentSnapshot은 비슷한데요, 둘다 1개의 문서를 의미하는 타입입니다.

 

근데 QueryDocumentSnapshot은 DocumentSnapshot이 될수있지만

DocumentSnapshot 은 QueryDocumentSnapshot 이 될수없습니다.

 

DocumentSnapshot 이 더 포괄적인 의미입니다.

( 교통수단 이라는 개념과 자동차 라는 개념이 있는데, 자동차 = 교통수단이 되지만, 교통수단 = 자동차가 될수없는 것과 같음) 

 

 

5.flutter 에서 firestore CRUD 해보기 <Read>

목차로돌아가기

 

대충 변수타입에 대해 익혔으니, 이제 화면에 보여지게해서 눈으로 확인해가며 CRUD 를 한번 해보겠습니다.

그리고 하나의 페이지에 모든 구성요소를 코딩해도되는데 , 뷰와 로직은 분리하는게 좋습니다. 그래서 분리해가며 할겁니다.

 

우리가 이 파트 전에 했었던 GetX를 이용해서 상태관리를 이용할거고

Vo 도 만들것이고 , Model도 만들거고

FireStore의 로직만 따로분리한 firestore_service 파일도 만들어서 할겁니다.

다~ 분리해서 할겁니다.

 

1. 먼저 Vo를 만들어주세요.

Post를 구성하는 Vo를 만듭니다.

저는 writerId, title, contents, keyword, isDeleted, createAt, updateAt 

이렇게 7개의 멤버변수를 가지도록 했습니다.

TimeStemp는 firestored에서 사용하는 시간타입 입니다.

keyword는 List형태인데 dynamic으로 받게 했습니다.

그이유는 파이어 스토어에서 리스트형태를 줄때는 dynamic으로 주기때문입니다.

 

fromDocumentSnapshot 이라는 메소드를 통해 DocumentSnapshot이 들어오면 각각의 키워드에 맞게 객체가 생성되도록 했습니다.

 

그리고 toMap 이라는 메소드도 만들었는데, 이건 파이어스토어에 업로드할때 Map<String,dynamic> 형태로 보내줘야 되기 때문입니다.

 

2. 모델을 만들어주세요.

모델의 내용을 보면 굳이 만들어야 되나 싶은데 , 만들어주세요. 

PostListModel 이라는 것을 만들었고 여기에 멤버변수는 PostVo로 구성된 postList 한개입니다.

 

fromQuerySnapshot을 통해 QuerySnapshot이 들어오면 for문을 통해 거기에 들어있는 하나하나 문서를 PostVo의 fromDocumentSnapsho을 통해 객체로 만든다음 postList에 담도록 했습니다.

(QuerySnapshot안에 문서하나하는 QueryDocumentSnapshot이지만 DocumentSnapshot으로 받아도 된다는건 위에서 설명함)

 

3. Firestore의 로직들이 있는 문서를 만들어주세요.

 

SaranFirebaseService 라고 만들어줬습니다. 

 

상단에 firesotre 인스턴스와 CollectionReference인 post 를 하나 선언하였습니다.

 

getPostList , getPostDetail 이라는 두개의 메소드를 만들어줬습니다.

 

1.getPostList는 isDeleted 필드가 false 인것을 조건으로하여 불러오도록하였습니다.

결과같이 N개인 QuerySnapshot으로 받게되고 PostListModel의 fromQuerySnapshot을 통해 PostListModel을 return하게 해줬습니다.

 

2.getPostDetail은 특정문서 1개를 얻는 메소드입니다.

그래서 매개변수로 docId를 받아주게 했고

결과값으로 DocumentSnapshot을 받게됩니다.

PostVo의 fromDocumentSnapshot을 통해 PostVo를 만들게 되고 그걸 return 하도록 해줬습니다.

 

4.  GetX를 이용해 데이터를 관리하는 상태관리 파일을 만들어주세요.

 

먼저 방금 만들어주었던 SaranFirebaseService 객체를 생성해줍니다.

그리고 postList 라는 변수를 만들어주었습니다.

 

그리고 리스트 부르는 것과 1개 부르는 메소드를 각각만들어줬습니다.

리스트 불러오는 함수는 리스트를 불러와서 postList에 담고 update를 통해 값이 변경되게 해주었고

상세불러오기는 그냥 이거 부르면 PostVo가 바로 리턴되도록 했습니다.

 

자 이렇게 4가지 과정을 끝냈으니

화면에서 한번 보도록하겠습니다.

 

그전에 

 

메인함수쪽에다가 Get.put 하는걸 잊지마세요~

 

콘솔에 기존의 연습으로 만들어두었던거 싹 지우고

한개만 손으로 만들어줍니다.

이때 각필드들은 우리가 만든 PostVo에 맞게 만들어주세요. (변수명 ,변수타입 모두) 아니면 오류납니다.

 

뷰 단에서

 

initState를 통해 시작하자마자 list를 불러오도록 했습니다.

 

build 부분에는 GetBuilder를 사용해서 우리가 4번에서 만든 상태관리 컨트롤러를 연결하였습니다.

이렇게 하니 firestore에 있는 내용이 불러와지는걸 볼수있습니다.

 

한개만 나오니까 좀 섭섭하죠. 그럼 여러개가 나오면 좋겠는데, 그럴려면 코딩으로 쓰기기능을 만들어서 한 10개만들어보면 쉽겠지만 아직 create를 안배웠으니 콘솔에서 직접 3개정도만 넣어보세요. 

그런데 필드가 7개만 되도 3개의 문서를 손으로 만들려니 이것도 꽤나 노동이네요.

그래서 문서를 복사해서 제목이나 타이틀같은거만 좀 바꾸면 되지않을까? 

그런 생각이 듭니다.

그래서 복사기능이 있나 봤더니 아무리해도 없네요.

문서의 더보기에 어떤기능이 있나 했더니 문서삭제와,문서필드 삭제말고는 없네요.

이건 firebase의 firestore 콘솔에는 아직 없는 기능입니다.

근데 방법이 있는데요,

 

 

Google Cloud의 추가기능을 눌러보시면 저렇게 4개의 기능들이 있네요. 이거중에 아무거나 누르면 google cloud 홈페이지로 들어가게됩니다. 눌러서 이동하기 누르면 뭐 나중에 유료로 전환계획이 있으십니까 그런거 물어보는데, 네 체크하고 들어가시면됩니다.

 

 

그럼 이런 화면이 나오게 됩니다. 우리가 firebase 콘솔에서 봤던 모습과 비슷한게 나오죠.

 

더보기 보면 무수한 서비스들이 나옵니다.

사실, GCP ( 구글 클라우드 플랫폼) 이라는 전체 서비스중에 firestore는 한개 서비스에 불과한 거였습니다.

유용한 다른 서비스들도 많으니, 나중에 실력좀 올라가면 하나씩 써보세요. 근데 아직AWS 가 1등인거 같긴합니다.

 

어쨌든 여기에는 비슷한 문서 추가 라는 기능이 있네요.

눌러서 비슷한 문서로 총 3개정도만 되도록 해보세요.

 

그럼 대충 이렇게 3개 불러와진게 보이실겁니다.

 

어쨌든 read 를 한번 해봤는데

파일도 여러개 만들고하니 너무 복잡하게 느껴질수도 있으실겁니다.

 

 

이해를 돕기위해 getPostList메소드 호출에 대한것을 그림으로 표현하자면 대략 이렇습니다.

 

1. 뷰단에서 상태관리에 있는 getPostList를 호출 -> 상태관리 컨트롤러 안에있는 getPostList가 실행됨.

2. 우리가 만든 FirebaseService의 getPostList 호출 -> FirebaseService에 있는 getPostList가 실행됨

3. fireStore에 쿼리 호출 -> fireStore에서 쿼리에 맞는 결과값 생성

4. fireStore에서 결과값을 QuerySnapShot 형태로 보내줌

5. PostListModel의 .fromQuerySnapshot에 QuerySnapshot을 넣어줌

6. querySnapshot이 n개의 DocumentSnapshot이 있기때문에 for문을 돌면서 PostVo의 fromDocumentSnapshot에 DocumentSnapShot을 넣어줌

7.만들어진 객체 n개를 for문 갯수대로 postListModel에 보내줌

8.만들어진 PostListModel을 firebaseService에 보내줌

9.8번에서 만들어진걸 PostListModel을 그대로 return함

10. 9번에서 그대로 넘어온 PostListModel의 postList만 뽑아서 데이터 업데이트함

11. 업데이트된 postList를 뷰단에서 빨때꼽아서 바로바로 사용함.

 

이렇게 되있습니다. 제가봐도 참 과정도많고 복잡해보이는데요.

이게 데이터 흐름구조입니다.

 

처음엔 어색하고 복잡할지 모르지만 하다보면 당연하게 받아들여질날이 올겁니다요.

 

--------

리스트 불러오기를 해봤으니, 저 리스트중에 한개를 누르면 Detail 페이지로 들어간다음

각각에 맞는 문서 1개를 불러오는걸 해보겠습니다.

 

이렇게 포스팅 리스트중에 1개를 누르면 PostDetailPage로 가는데 매개변수로 문서의 id를 넘겨줘서, 그 id로 InitState에서 특정문서찾기 메소드를 발동시켜 특정 문서를 찾도록 할것입니다. 

 

1개를 불러오기 위해서는 1개 문서의 id를 알아야합니다. 

그런데 우리는 postVo에 문서 id 정보를 담지 않았습니다. 그래서 postDetailPage에 문서 id정보를 넘겨줄수가 없습니다.

그래서 Vo를 좀 수정해야합니다.

vo에서 docId라는걸 하나 추가했습니다.

 

그럼 이제 PostDetailPage에 docId정보를 넘겨줄수있겠죠.

 

PostDetailPage에서 docId를 받아서 상세페이지를 구성하는걸 해봤습니다.

getDetailPost라는 메소드를 만들어서 특정문서를 불러오게 했습니다. 불러온값을 post변수에 저장하도록 했고, 그값을 사용해서 화면에 보여주게 만들었습니다.

 

이렇게 Read 는 해보았습니다.

그런데 우리가 콘솔에서 넣고 읽는건 별로 의미가 없을것입니다.

관리자는 콘솔에서 값넣을수있는데 일반 유저들은 글을 작성하거나 했을때 콘솔에 직접넣거나 그럴수는 없을거기 때문이죠.

그래서 다음은 Create를 해보겠습니다.

 

6.flutter 에서 firestore CRUD 해보기 <Create>

목차로돌아가기

 

메인페이지에서 글작성하는 페이지로 넘어가는 버튼을 하나 만들었습니다.

저는 floatingActionButton 이라는 Scaffold위젯의 옵션을 이용해서 만들었습니다.

 

UI는 대충 만들어 봤는데요 TextField 라는 위젯을 이용해서 글을 입력할수있게 했습니다.

지금 하려는 예시에서는 TextController는 없어도 되는데 , 그냥 TextField를 쓸때는 controller를 사용하는걸 디폴트라고 생각합시다.

controller를 썼으면 dispose 에서 controller를 삭제해줘야합니다.

dispose는 StateFul위젯이 죽을때 즉 없어질때 발생하는 메소드로 라이플 사이클 중에 하나입니다.

(initState는 StateFul위젯이 생성될때 발생하는메소드. 라이프사이클 강의때배웠죠)

 

onChange를 이용해 사용자가 입력한 글의 변화가 발생하면 setState를 이용해서 title 이나 contents 변수가 바뀌도록 해두었습니다.

 

글쓰기 버튼을 만들었는데 저는 ElevatedButton이라는 위젯을 이용해서 버튼을 만들었습니다. 버튼같은거 만들때 기본적인 ui를 세팅해둔 위젯이죠.

(그냥 일반 위젯인 Container로 만든다음 GestureDetector로 버튼만들어도 상관없습니다.)

 

어쨋든 이걸 누르면 postVo 인스턴스를 생성한다음 그인스턴스에 값들을 넣고 postController의 writePost라는 메소드를 작동시키게 해두었습니다. writePost는 매개변수로 우리가만든 userPost를 받도록 해주었구요.

(writerId는 그냥 아무문자열이나 넣었습니다. 실제로는 로그인을 통해 그 로그인된 사용자의 이메일이라던지 정보를 넣어야겠죠.)

 

그래서 writePost가 작동해서 데이터베이스에 값넣는게 완료되면, 다시 postList를 불러와서 postlist를 갱신해주게 했고, 그작업이 끝아면 Get.back을 통해 WritePostPage에서 벗어나도록 했습니다.

 

postController에 writePost 메소드가 없으니 만들어 주도록 해봅시다.

postController에 writePost라는 메소드를 만들어 주었고,

saranFirebaseService.writePost() 를 발동시키게 해주었습니다. 매개변수로 PostVo 타입의 userPost를 그대로 넘겨주게했구요.

 

마찬가지로 saranFirebaseService에도 writePost메소드를 만들어 주었습니다.

 

여기서 Create 방식에는 add 와 set 두개의 방식이 있는데,

add는 데이터베이스에 문서가 올라갈때 문서의 docId를 자동으로만들어 데이터베이스에 넣는방식이고,

set은 데이터베이스에 올라가기전에 먼저 docId를 만든다음 데이터베이스 올리는 방식입니다.

두개다 상관없지만 docId를 내가 임의로 만들고 싶거나, docId 정보를 문서필드의 한부분으로 넣고싶거나 할때 사용하면되겠죠.

 

어쨋든 우리는 간단히 add를 이용해서 업로드 합니다.

그런데 여기서 중요한점은 userPost 로 넘어온걸 그대로 넣으면 안되고, 이걸 map 형태로 바꿔서 올려야합니다.

그래서 우리가 PostVo에 toMap 이라는 메소드를 만들어 줬었죠.

 

어쨋든 이렇게 작성하고나서 글쓰기 버튼을 눌렀더니 데이터베이스에 기록이 됩니다

그리고 Get.back으로 페이지가 나가지면서 리스트화면으로 돌아왔을때 이렇게 리스트에 추가되어있는 모습이 보일것입니다.

 

그런데 이상한점은 가장최근에 작성했는데 리스트에서는 3번째에 표시되고 있습니다.

그이유는 firestore에서 데이터를 가져올때 랜덤문자열인 docId 기준으로 정렬해서 가져오기 때문입니다.

우리는 최근에 작성된게 제일 위에 올라갈것으로 예상했는데, 그게안되어 있는거죠 그래서 리스트를 불러올때 조건을 하나 줘야하는데

그게 orderBy 입니다.

그래서 리스트를 불러오는 쪽에가서 orderBy("createAt") 으로 해줍니다.

이건 우리가만든 문서안에 createAt 필드중 최신날짜 기준으로 정렬해서 불러와라 라는 의미입니다.

 

그랬더니 에러가 뜨면서 리스트에는 아무것도 안보이게 되는데요, 이 문제는 이 강의 맨 위쪽에 제시한 fireStore의 단점중에 하나로 다중조건일때는 index를 만들어 해결할수있다고 했었습니다.

 

색인(인덱스) 탭으로 와서 색인 만들기를 눌러줍니다.

그리고 어떤기준으로 색인를 만들거냐에 값을 넣어주시고 색인만들기를 눌러줍니다.

그러면 생성중 이라고 뜨면서 색인이 만들어집니다. 이건 현재 데이터가 얼마나 있는지에 따라 시간걸리는 정도가 다른데, 지금 상태에서는 데이터가 별로없으니 금방 만들어 질겁니다. 한 2분정도?

그리고 특이점이 색인생성버튼을 누르면 조금있다가 __name__이라는것도 생기는데 이건 우리가 지정안해도 자동으로 만들어지는데 이건 자동이니까 그냥 냅두시면 됩니다.

 

이렇게 생성중 -> 사용 설정 완료됨으로 바뀌면 정상적으로 사용할수있습니다.

그러면 이렇게 가장 최근에 쓴 글이 위에 와져있는게 보일겁니다.

 

이렇게 create 를 해보았습니다.

 

7.flutter 에서 firestore CRUD 해보기 <Update>

목차로돌아가기

 

 

이번엔 수정을 해볼겁니다. 글을썻으니 수정기능이 당연 있어야 할것입니다.

 

대충 내용수정하기 버튼을 누르면 해당 docId를 찾아서 내용을 "apapapap 이렇게수정" 이라는 문자열로 수정해보겠습니다.

대충하는것은 그냥 update하는걸 보여줄려고하는겁니다. 원래라면 TextField써서 수정내용을 진짜로 적고 해야겠지만 의미없겠죠

 

어쨋든 같은방식으로 updatePost라는 메소드를 발동시키고 매개변수로 docId 와 수정된 contents 를 주겠습니다.

 

PostController와 SaranFirebaseService에

updatPost 메소드를 만들었습니다.

그럼 이렇게 데이터베이스가 수정되면서 UI도 수정되어 보이게 될것입니다.

 

 

8.flutter 에서 firestore CRUD 해보기 <Delete>

목차로돌아가기

 

 

삭제를 해볼것인데요, 삭제에는 논리삭제와 물리삭제가 있습니다.

논리삭제는 update를 이용해서 isDeleted 필드를 false -> true 로 바꿔줘서 postList를 부를때 isDelete = false인것만 불러오게하면

실제로 데이터베이스에서 문서가 삭제가 된것은 아니지만, 필터링으로 안불러오기때문에 삭제된것처럼 보이게 하는것입니다.

많은 서비스들이 이렇게 하는데, 데이터베이스에 한번들어간 내용은 함부로 지우지않기때문에 이렇게 논리삭제로 많이합니다.

회원탈퇴 같은거 할때도 실제로 값이 삭제되는게 아니라 한개의 필터만 수정하는 형식으로 해두었다가, 3개월이 지나면 user 를 삭제한다거나 합니다. 회원가입할때 개인정보보호정책 같은거 보면 그렇게 한다는 내용이 들어가 있을겁니다.

 

어쨌든 논리삭제의 방식의 update는 위에서 해보았으니 이번엔 실제로 데이터베이스에서 문서를 아에 삭제해버리는 물리삭제를 해보겠습니다.

 

이것도 대충 이렇게 만들어 봤습니다.

deletePost라는 매소드를 만들고 매개변수로 docId를 줬습니다.

 

postController와 service 부분에 deletePost 메소드를 만들어주세요.

삭제버튼을 누르니, 해당 문서가 삭제된것이 보일겁니다.

데이터베이스에도 4개 -> 3개로 줄어들었죠.

 

이렇게 firestore를 이용하여 CRUD 를 모두 해보았습니다.

 

다음 시간에는 auth , storage 에 대해 해보겠습니다.

flutter 현재 프로젝트 그대로 사용할게요.

 

'코딩생초보를 위한 플러터 빠르게 한바퀴' 카테고리의 다른 글

9.distribution  (0) 2023.10.27
8.firebase2-auth,storage  (0) 2023.10.21
6.network  (0) 2023.08.22
5.state management  (0) 2023.08.11
4.Json  (0) 2023.08.09