-
[개발일지] flutter 앱개발 5주차Study/Development 2022. 10. 3. 15:33
<스파르타코딩 flutter 강의 수강일지>
5주차: Firebase 로그인 구현 및 데이터베이스 연동
1. Firebase 사용하기 위한 패키지
// 패키지 설치법 flutter pub add <패키지명> // 패키지 목록 firebase_core : firebase 사용을 위한 필수 패키지 firebase_auth : firebase 로그인 패키지 cloud_firestore : firebase 데이터 베이스 패키지 provider : 상태 관리 패키지
강의에서는 패키지를 한꺼번에 pubspec.yaml 파일에 추가했지만, 버전 문제로 하나씩 직접 설치하는것이 좋을 것 같다.
2. Firebase 연결
https://console.firebase.google.com/u/1/?hl=ko
로그인 - Google 계정
이메일 또는 휴대전화
accounts.google.com
Firebase 콘솔에 들어가 새로운 프로젝트를 만든다.
진행 중에 안내되는바와 같이 Android, iOS 앱 추가를 완료한 후, 패키지를 설치하면 된다.
몇몇의 과정은 앱 추가 중에 생략해도 되는데, 이는 패키지를 설치하기 때문이다.
*생략 해도 되는 과정(iOS)
3) Firebase SDK 추가
4) 초기화 코드 추가
설치가 완료되면, firebase_core패키지의 함수를 맨 처음에 실행해준다.
// Firebase를 사용하기 위한 실행 함수 void main() async { WidgetsFlutterBinding.ensureInitialized(); // main 함수에서 async 사용하기 위함 await Firebase.initializeApp(); // firebase 앱 시작 runApp(const MyApp()); }
*Firebase에 연결 후에, 나의 iOS 에뮬레이터가 동작하지 않고, 아래와 같은 오류가 발생했다.
이 문제는, Xcode 시뮬레이터의 기기를 변경하여 해결했다. (iPhone 13 Pro로 변경)
찾아보니, API에 관련된 버전과 기기의 버전이 맞지 않아서 일 수 있다고 한다.
Simulater 우클릭 > Device > iOS 15.5 > 다른 기기 선택 3. Firebase Auth: 로그인 기능 구현
다소 번거로운 로그인 기능 구현을, firebase_auth 라는 패키지를 통해 쉽게 구현할 수 있다.
Firebase Console 웹 페이지에 접속하여
우측 메뉴 > 빌드 > Authentication 메뉴에서 시작하기를 누른 후 과정을 거치면 된다.
auth_service.dart 라는 파일을 만들어, provider 패키지를 이용해 main에 적용해주었다.
import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; class AuthService extends ChangeNotifier { User? currentUser() { // 현재 유저(로그인 되지 않은 경우 null 반환) return FirebaseAuth.instance.currentUser; } // 회원가입 void signUp({ required String email, // 이메일 required String password, // 비밀번호 required Function onSuccess, // 가입 성공시 호출되는 함수 required Function(String err) onError, // 에러 발생시 호출되는 함수 }) async { // 회원가입 // 이메일 및 비밀번호 입력 여부 확인 if (email.isEmpty) { onError("이메일을 입력해 주세요."); return; } else if (password.isEmpty) { onError("비밀번호를 입력해 주세요."); return; } // firebase auth 회원 가입 try { await FirebaseAuth.instance.createUserWithEmailAndPassword( email: email, password: password, ); // 성공 함수 호출 onSuccess(); } on FirebaseAuthException catch (e) { // Firebase auth 에러 발생 onError(e.message!); } catch (e) { // Firebase auth 이외의 에러 발생 onError(e.toString()); } notifyListeners(); } // 로그인 void signIn({ required String email, // 이메일 required String password, // 비밀번호 required Function onSuccess, // 로그인 성공시 호출되는 함수 required Function(String err) onError, // 에러 발생시 호출되는 함수 }) async { // 로그인 if (email.isEmpty) { onError('이메일을 입력해주세요.'); return; } else if (password.isEmpty) { onError('비밀번호를 입력해주세요.'); return; } // 로그인 시도 try { await FirebaseAuth.instance.signInWithEmailAndPassword( email: email, password: password, ); onSuccess(); // 성공 함수 호출 notifyListeners(); // 로그인 상태 변경 알림 } on FirebaseAuthException catch (e) { // firebase auth 에러 발생 onError(e.message!); } catch (e) { // Firebase auth 이외의 에러 발생 onError(e.toString()); } } // 로그아웃 void signOut() async { // 로그아웃 await FirebaseAuth.instance.signOut(); notifyListeners(); // 로그인 상태 변경 알림 } }
*Provider 설정하기
MultiProvider를 MyApp 상위에 만들어 준 뒤,
ChangeNotifierProvider를 설정하여, 앱이 빌드될 때 새로고침 및 데이터를 전달한다.
runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (context) => AuthService()), ], child: const MyApp(), ), );
4. Firestore 데이터베이스 연동하기
Firestore의 데이터베이스를 연동하여, CRUD 기능을 구현하였다.
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; // firebase의 bucket이라는 이름의 Collection을 가리키는 변수 생성 // 빌드 시 실행 class BucketService extends ChangeNotifier { final bucketCollection = FirebaseFirestore.instance.collection('bucket'); // Read: 내 bucketList 가져오기 Future<QuerySnapshot> read(String uid) async { return bucketCollection.where('uid', isEqualTo: uid).get(); } // Create: bucket 만들기 void create(String job, String uid) async { await bucketCollection.add({ 'uid': uid, // 유저 식별자 'job': job, // 할 일 'isDone': false, // 완료 여부 }); notifyListeners(); // 갱신 } // Update: bucket isDone 업데이트 void update(String docId, bool isDone) async { await bucketCollection.doc(docId).update({'isDone': isDone}); notifyListeners(); // 화면 갱신 } // Delete: bucket 삭제 void delete(String docId) async { await bucketCollection.doc(docId).delete(); notifyListeners(); } }
*보안 규칙 설정
누구나 CRUD를 할 수 없도록 Firestore 보안 규칙을 설정해야 함
Firebase Console > Firestore > 규칙 탭
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /bucket/{document} { // document의 uid와 일치하는 유저만 RUD 가능 allow read, update, delete: if request.auth != null && request.auth.uid == resource.data.uid; // Create의 경우, 로그인 되어있으면 가능 allow create: if request.auth != null; } } }
// bucket 밑에 있는 docment에 있는 uid 값과 요청자의 uid가 일치하는 경우만 CRUD가 가능 * request.auth == null : Firebase Auth 로그인 하지 않음 * request.auth != null : Firebase Auth 로그인 됨 * request.auth.uid : 로그인한 유저의 uid * resource.data.<문서 field 이름> : 직접 정의한 document 상의 field 이름(여기에선 버킷을 작성할 때 추가한 uid)
알아둬야 할 것들
1. FutureBuilder
플러터와 다트는 본질적으로 동기화가 되지 않는다. (asynchronous)
다트의 Future를 사용하면, 스레즈나 교착상태를 걱정할 필요 없이 IO를 관리할 수 있다. (Async makes IO easy)
아래와 같이 FutureBuilder를 이용할 수 있다.
FutureBuilder는 미래의 현황을 쉽게 결정하게 하고, 정보를 불러오는 동안/ 가능할 때 어떤걸 보여줄 지 선택하도록 한다.
/// FutureBuilder 사용법. FutureBuilder( future: http.get('http://awsome.data'), builder: (context, snapshot) { // connectionState로 Future의 상태를 확인 if (snapshot.connectionState == ConnectionState.done) { // Future가 해결되는 동안오류가 발생했는지 확인. if (snapshot.hasError) { return SomethingWentWrong(); } // 연결이 완료되었을때 보여줄 것. return AwesomeData(snapshot.data); } else { // Future가 바쁠 때(연결중), 적절한 위젯 배치. return CircularProgressIndicator(); } } )
버킷리스트를 Firestore에서 조회해 온 뒤 실행해야 하기 때문에,
ListView.builder()를 FutureBuilder로 감싸줬다.
// FutureBuilder child: FutureBuilder<QuerySnapshot>( future: bucketService.read(user.uid), builder: (context, snapshot) { final docs = snapshot.data?.docs ?? []; if (docs.isEmpty) { return Center( child: Text('버킷리스트를 작성해주세요.'), ); } return ListView.builder( itemCount: docs.length, itemBuilder: (context, index) { final doc = docs[index]; String job = doc.get('job'); bool isDone = doc.get('isDone'); return ListTile( title: Text( job, style: TextStyle( fontSize: 24, color: isDone ? Colors.grey : Colors.black, decoration: isDone ? TextDecoration.lineThrough : TextDecoration.none, ), ), // 삭제 아이콘 버튼 trailing: IconButton( icon: Icon(CupertinoIcons.delete), onPressed: () { // 삭제 버튼 클릭시 bucketService.delete(doc.id); print('$index 번째 버킷 삭제'); }, ), onTap: () { // 아이템 클릭하여 isDone 업데이트 bucketService.update(doc.id, !isDone); print('$index 번째 버킷 클릭 $isDone'); }, ); }, ); })
2. 삼항 연산자
*짤막한 복습
isDone이 true인지 false인지에 따라, 취소선 + 그레이 텍스트 스타일을 적용하였다.
style: TextStyle( fontSize: 24, color: isDone ? Colors.grey : Colors.black, decoration: isDone ? TextDecoration.lineThrough : TextDecoration.none, ),
반응형'Study > Development' 카테고리의 다른 글
[개발일지] Tensorflow 딥러닝_딥러닝이론 (0) 2023.01.02 [개발일지] Python 기초문법 (0) 2022.12.31 [개발일지] flutter 앱개발 4주차 (0) 2022.10.02 [개발일지] flutter 앱개발 3주차 (1) 2022.09.23 [개발일지] flutter 앱개발 2주차 (0) 2022.09.12