Programming-[CrossPlatform]/Flutter

Flutter 기본-20. 채팅앱 - firebase database, 규칙 등록, authStateChanges

컴퓨터 탐험가 찰리 2023. 2. 12. 18:59
728x90
반응형

Youtube 코딩셰프님의 강의를 요약 정리한 글이다. dart 언어나 이론 부분은 자바와 유사하여 대부분 제외하였고, flutter 기초 위주로 정리한다.

https://www.youtube.com/@codingchef

 

코딩셰프

향후 대세가 될 플러터를 단계별로 맛있게 학습하실 수 있습니다!

www.youtube.com

 

 

 

background image reference : https://wallpapercave.com/cartoon-chickens-wallpapers

 


 

1. username 데이터베이스에 등록하기(추가 데이터 데이터베이스에 넣기)

 

email, password는 firebase_auth 패키지가 담당했으나, 추가적인 정보는 cloud_firestore가 담당한다. user에 대한 추가정보를 넣기 위해서 파이어베이스 웹 사이트에서 직접 넣는 것이 아니라 코드를 통해 파이어베이스에 바로 컬렉션을 추가한다.

 

아래 코드에서는 Signup쪽에서 회원 가입 시 user collection을 추가해준다.

FirebaseFirestore.instance.collection({컬렉션 이름}).doc({문서 ID}).set({설정할 필드값들})

userName 외 추가로 userEmail 값도 넣어주었다. set 메서드를 통해 등록할 수 있고, Map 타입으로 데이터를 넣어야된다는 것도 기억하자.

 

if (isSignupScreen){
  _tryValidation();
  try {
    final newUser = await _authentication.createUserWithEmailAndPassword(
        email: userEmail, password: userPassword
    );

    if(newUser.user != null) {

      await FirebaseFirestore.instance.collection('user').doc(newUser.user!.uid)
          .set({
        'userName' : userName,
        'email' : userEmail,
          });

      Navigator.push(context, MaterialPageRoute(builder: (context){
        return ChatScreen();
      }),
      );
      setState(() {
        showSpinner = false;
      });
    }

 

회원가입을 해보면 데이터베이스에 user 컬렉션이 추가되고, user의 uid로 문서가 만들어진다. 그리고 내부에는 email, userName값이 필드로 잘 추가된 것을 확인할 수 있다.

 

 

 

2. 규칙 업데이트

 

프로젝트 ID와 데이터베이스의 주소를 안다면 누군가가 데이터베이스에 접근해서 데이터를 훔쳐가거나 업데이트해버릴 수도 있다. 이런 보안상의 이유로 설정하는 것이 데이터베이스 규칙이다. 또한 데이터베이스에 누가 접근했고, 어떤 내용을 썼는지 알 수 있게 설정해주는 기능도 한다.

 

규칙 항목에서 아래와 같이 설정할 수 있다. match 이후 컬렉션 이름을 써준다. document=** 구문은 모든 문서에 접근하게 한다는 의미이다. 만약 모든 규칙을 지워버리면 인증받은 사용자라 할지라도 접근이 불가능하다.

 

allow 이후 허용 기능을 추가해주면 되는데, create와 write 중 write는 create, update, delete를 포함하는 개념이다. 또한 if문으로 허용되는 메서드에 대한 조건을 추가해줄 수 있다. if request.auth != null 이라고하면 인증받지 않은 사용자는 권한이 없게 지정할 수 있는 것이다.

 

아래 예제는 user 컬렉션에 있는 각 사용자에 대한 정보는 인증을 받았고, 사용자를 식별하는 uid가 일치하는 사용자에 한해서만 read, write 권한을 주는 것이다. 즉 본인만 본인 정보를 조회 및 수정할 수 있게된다. 추가로 인증을 거친 일반 유저들은 user 정보에 read는 할 수 있도록, /chats 컬렉션에서는 모든 문서에 대해서 read, create는 할 수 있도록 설정해주었다.

 

부가적으로 원래 규칙을 수정하지 않은 채로 놔두면 파이어베이스에서 보안 규칙에 대한 경고 이메일이 주기적으로 오게 되는데, 수정하면 이메일이 더 이상 오지 않는다고 한다.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
  
    match /user /{uid} {
    allow read, write: if request.auth != null && 
      request.auth.uid == uid;
    }
    
    match /user {
    	allow read: if request.auth.user != null;
    }
  
    match /chats /{document=**} {
      allow read, create: if true;
    }
  }
}

 

 

 

 

 

3. 에러 수정, state 관리

 

로그인 폼에 남아있는 텍스트 지워주기: authStateChanges

부가적으로 로그인 후 로그아웃 시에도 남아있는 텍스트를 지워준다. 사실 이렇게 되는 것은 보안상 큰 문제가 발생할 수 있는 중요한 문제이다.

 

파이어베이스는 로그인 시 사용자의 로그인 정보를 관리하기 위해서 토큰을 발행한다. 사용자는 이 토큰을 바탕으로 인증 처리를 하면서 기능들을 사용할 수 있게 된다. 이 원리를 바탕으로 위젯을 로딩할 수 있도록 코드를 변경할 것이다.

 

사용자의 인증(토큰) 상태 변화 감지

main.dart에서 home 인자에 LoginSignupScreen() 대신 StreamBuilder를 넣어준다. 그리고 필수 인자값인 stream, builder를 지정해준다. stream 인자에는 FirebaseAuth.instance.authStateChanges()를 사용하는데, 이 메서드는 Firebase에서 지원하는 사용자의 auth 상태가 바뀔 때 stream을 넣어주어 감지할 수 있게 해주는 세 가지 메서드(authStateChanges, idTokenChanges, userChanges) 중 하나이다. 세 메서드에 대한 정보는 아래 문서에서 확인할 수 있다. FirebaseAuth를 사용하기 위해서 firebase_auth 패키지를 import 해야한다.

https://firebase.flutter.dev/docs/auth/usage/

 

stream으로 감지한 결과가 있을 때, builder가 작동하도록 하는 구조이다. stream에서 auth 상태 변화가 감지된다면 snapshot.hasData에 데이터가 있게 되고, 이 때는 ChatScreen()을 리턴해준다. 그렇지 않은 경우 LoginSignupScreen을 리턴해준다.

 

@override
Widget build(BuildContext context) {
  return MaterialApp(
    debugShowCheckedModeBanner: false,
    title: 'chatting',
    home: StreamBuilder(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (BuildContext context,
          AsyncSnapshot<dynamic> snapshot) {
        if(snapshot.hasData){
          return const ChatScreen();
        } else {
          return const LoginSignupScreen();
        }
      },
    ),
  );
}

 

 

 

위젯 중복 호출 제거하기

 

이제 위 영상처럼 로그인 후 로그아웃을 해도 입력 폼에 아무 글자가 없는 LoginSignupScreen 위젯이 로딩되는 것을 확인할 수 있다. 그런데 자세히 보면 매우 찰나의 순간이지만 로그인 시에 해당 위젯이 2번 로딩되고 있다.

 

 

실제 flutter inspector로 살펴봐도, ChatScreen이 2개가 위젯 트리에 등록된 것을 볼 수 있다. 이 상태에서는 뒤로가기를 눌러도 1개의 ChatScreen은 pop이 되지만, 나머지 ChatScreen이 남아있게 된다. 이 상태에서 로그아웃 버튼을 누르면 검은 화면이 되면서 버그 화면처럼 되버린다.

 

 

이 문제의 원인은 상기 로그인 시 유저의 auth 상태가 바뀌며 authStateChanges에 의해 ChatScreen을 1번 불러오고, 기존에 main_screen에서 로그인을 했을 때 Navigator.push를 통해 ChatScreen을 한 번 더 불러왔기 때문이다.

 

또한 이것은 여태까지 작성한 화면 구성이 Stack 위젯을 사용해서 화면을 하나씩 쌓아올리고 로그아웃을 했을 땐 Navigator.pop을 통해 Stack에 쌓인 위젯을 제거하는 방식으로 구성했기 때문이다.

 

 

이를 해결하기 위해서 main_screen.dart에서 로그인 시 ChatScreen으로 이동하는 코드, chat_screen.dart에서 로그아웃 시 Navigator.pop를 적용했던 코드를 삭제해준다.

 

 

 

이제 ChatScreen에서도 뒤로가기 버튼이 보이지 않고, 로그아웃 시 텍스트 폼에 입력된 모든 정보가 삭제된 채로 돌아온다.

 

이것은 main.dart에 적용된 StreamBuilder 부분에서 로그아웃 시 Firebase가 유저가 갖고 있던 token을 삭제해주고 authStateChanges가 감지하는 Stream에는 더이상 정보가 남지 않게 되어 snapshot.hasData가 False 처리되면서 LoginSignupScreen()을 리턴하게 되는 것이다.

 

다른 것은 신경쓸 필요없이 유저의 로그인(토큰) 상태만 갖고 화면 처리를 한다는 점에서 편하다. 이 방식을 잘 기억해두면 Firebase 외 다른 데이터베이스를 이용할 때도 사용할 수 있을 것 같다.

728x90
반응형