Flutter와 Firebase로 Android iOS 둘 다 만들기 6 구글&파이어베이스로 사용자 인증하기 1

4 분 소요

Fire auth(Firebase authentication)를 이용해 Google 및 이메일 인증을 해봅니다.

개요

Fire auth는 일관성 있는 사용자 관리 솔루션입니다.

사용자 로그인 관련 각기 다른 코딩과 개념으로 복잡, 뒤죽박죽인 것을 파이어베이스가 통일한 것이죠.

Fire auth를 이용해서 웹사이트, 모바일, 데스크탑등 모두 일관성 있는 회원가입 및 로그인을 구현할 수 있습니다.

Fire auth는 다양한 로그인 방법으로 로그인이 가능합니다.

alt auth methods

다양한 로그인 방법 중에서 이메일과 구글 인증을 다뤄볼 것입니다.

이메일로 일반적인 방법을, 구글인증으로 프로바이더를 통해 인증하는 방법을 한번 구현해보면 대부분의 인증은 같은 개념으로 접근 할 수 있습니다.

그 중 먼저 하려고 하는 것은 구글 인증입니다.

플러터 업데이트

이제부터 외부 패키지들과 연결되어야 하기 때문에 항상 최신 버전으로 유지 해놓는 것이 좋습니다.

$ flutter upgrade
Flutter 1.12.13+hotfix.5 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 27321ebbad (2 days ago) • 2019-12-10 18:15:01 -0800
Engine • revision 2994f7e1e6
Tools • Dart 2.7.0

현재 최신 버전인 1.12.13 버전으로 시작합니다.

패키지 설치

외부 패키지들은 모두 https://pub.dev에서 사용 가능합니다.

node의 npmjs.com과 비슷한 곳입니다.

두가지 패키지가 필요합니다.

간단한 원리는 google로 로그인 해서 얻은 인증정보(credential)로 firebase 인증을 하는 것입니다.

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  # firebase_auth: ^0.15.2
  firebase_auth:
  google_sign_in:

pubspec.yaml 파일을 열어서 2 패키지를 추가 작성 해놓고 저장하면 최신버전으로 설치됩니다.

사실 자동으로 flutter pub get 이라는 명령이 들어가는 것입니다.

마치 node의 npm install 같은 것이죠.

버전을 쓰지 않으면 최신버전으로 설치됩니다.

호환성 문제 때문에 가급적 버전을 써주는 것이 좋습니다. 하지만 저처럼 최신버전 성애자는 쓰지 않습니다~

비동기 관련

비동기에 대해 간단히 알고 넘어가야 할 것이 있습니다.

로그인 관련 행동을 할 때 모두 시간이 걸린다는 것입니다.

시간이 걸리는 것은 순차적인 흐름의 코딩이 어렵게 됩니다.

print(longTimeFunc()); // 3초가 걸리는 함수라고 가정
print('hi');

longTimeFunc() 의 결과 출력보다 ‘hi’가 먼저 나오게 됩니다. longTimeFunc()가 선행되어야할 로직이라면 매우 곤란하겠죠..

이것에 관한 설명은 공식홈에 아주 잘 정리되어있으니 꼭 참고하시기 바랍니다.

참고: https://dart.dev/codelabs/async-await

참고로 ecma-sciprt의 async await 개념과 거의 90% 이상 비슷합니다. 최근 언어의 동향과도 같은 것이니 숙지하면 다른 언어도 이러한 비슷한 개념에서 해메이지 않게 됩니다.

패키지 사용

./lib/main.dart

import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart'; // added
import 'package:firebase_auth/firebase_auth.dart'; // added

상단에 import 하면 해당 모든 기능들을 사용하게 됩니다.

로그인

순서는 구글 인증 후 파이어베이스 인증입니다.

구글

공식 홈의 예제를 보고 필요한 부분만 작성합니다.

참고 예제: https://github.com/flutter/plugins/blob/master/packages/google_sign_in/google_sign_in/example/lib/main.dart

변수 선언(인스턴스 생성)

final GoogleSignIn googleSignIn = GoogleSignIn();

먼저 구글 인증을 하기 위한 변수를 만들어 둡니다. 아직 아무 일도 일어나지 않습니다.

로그인

final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();

구글 로그인 팝업창을 띄우고 로그인의 결과를 googleSignInAccount에 담습니다.

여기까지만 하면 사실 구글 로그인은 끝입니다.

하지만 우리는 구글 인증 정보를 가지고 파이어베이스 인증을 해야합니다.

인증 정보 받기

final GoogleSignInAuthentication googleSignInAuthentication =
        await googleSignInAccount.authentication;

구글 인증 정보를 받아둡니다.

신뢰 정보 받기

final AuthCredential credential = GoogleAuthProvider.getCredential(
  accessToken: googleSignInAuthentication.accessToken,
  idToken: googleSignInAuthentication.idToken,
);

신뢰 정보를 받아둡니다.

파이어베이스

참고 예제: https://github.com/FirebaseExtended/flutterfire/blob/master/packages/firebase_auth/firebase_auth/example/lib/signin_page.dart

위에 받아 놓은 신뢰정보(credential)로 로그인 하면 끝입니다.

변수 선언(인스턴스 생성)

final FirebaseAuth _auth = FirebaseAuth.instance;

로그인

final AuthResult authResult = await _auth.signInWithCredential(credential);

인증 정보 담아두기

final FirebaseUser user = authResult.user;

결국 원하는 정보는 바로 FirebaseUser user 입니다.

로그아웃

로그아웃은 두종류로 할 수 있습니다.

구글

구글 로그아웃은 완전한 로그아웃입니다.

로그아웃 후에 다시 로그인을 할 때 로그인 창이 다시 뜹니다.

googleSignIn.signOut();

파이어베이스

파이어베이스만 로그아웃합니다.

로그아웃 후에 다시 로그인을 할 때 로그인 창이 다시 뜨지 않습니다.

_auth.signOut();

googleSignIn.signIn()할 때 이미 로그인 되어 있어서 값만 넘겨주기 때문이겠죠..

적용하기

전체 소스

./lib/main.dart

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';

void main() { 
  runApp(MyApp());
}

class MyApp extends StatelessWidget {  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeWidget()
    );
  }
}

class AppState {
  bool loading;
  FirebaseUser user;
  AppState(this.loading, this.user);
}

class HomeWidget extends StatefulWidget {
  @override
  _HomeWidgetState createState() => _HomeWidgetState();  
}

class _HomeWidgetState extends State<HomeWidget> {
  final app = AppState(false, null);  
  @override
  Widget build(BuildContext context) {
    if (app.loading) return _loading();
    if (app.user == null) return _logIn();
    return _main();
  }
  Widget _loading () {
    return Scaffold(
      appBar: AppBar(title: Text('loading...')),
      body: Center(child: CircularProgressIndicator())
    );
  }
  Widget _logIn () {
    return Scaffold(
      appBar: AppBar(
        title: Text('login page')
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[   
            RaisedButton(
              child: Text('login'), 
              onPressed: () {
                _signIn();
              }
            )
          ],
        )
      )      
    );
  }
  Widget _main () {
    return Scaffold(
      appBar: AppBar(
        title: Text('loginsss'),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.account_circle),
            onPressed: () {
              _signOut();
            },
          )
        ],
      ),
      body: Center(child: Text('contents'))
    );
  }

  final GoogleSignIn googleSignIn = GoogleSignIn();
  final FirebaseAuth _auth = FirebaseAuth.instance;

  Future<String> _signIn() async {
    setState(() => app.loading = true);
    final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
    final GoogleSignInAuthentication googleSignInAuthentication =
        await googleSignInAccount.authentication;

    final AuthCredential credential = GoogleAuthProvider.getCredential(
      accessToken: googleSignInAuthentication.accessToken,
      idToken: googleSignInAuthentication.idToken,
    );

    final AuthResult authResult = await _auth.signInWithCredential(credential);
    final FirebaseUser user = authResult.user;
    print(user);

    setState(() {
      app.loading = false;
      app.user = user;
    });

    return 'success';
  }

  void _signOut() async{
    await googleSignIn.signOut();
    // await _auth.signOut();
    setState(() {
      app.user = null;
    });
  }
}

설명

코드를 보면 비동기가 왜 필요한지 대충 알 수 있습니다.

다 시간이 걸리는 함수들인데 윗줄이 선행되야 의미가 있는 코드들이어서 await로 다 기다려야 하는 것입니다.

await를 사용하려면 해당 함수가 async여야 합니다.

그리고 아직 반환될 리턴은 의미가 없지만 비동기로 받기 위해 Future를 사용한 것입니다.

자스의 promise 리턴 같은 개념입니다.

실제 적용 화면

alt success

마치며

안타깝게도 위의 코드는 현재 동작하지 않습니다.

로그인을 하는 즉시 에러가 나버리기 때문이죠..

왜냐하면 구글, 파이어베이스 세팅이 되어 있어야만 동작합니다.

물론 위의 적용 화면은 세팅이 되어 있어서 가능한 것입니다.

그래서 이제부터는 코드만으로 어떻게 해볼 수 없는 영역이 있어서 조금 힘들어집니다.

소스코드

https://github.com/fkkmemi/ff

영상

댓글남기기