Flutter를 사용하면, Firebase Authentication을 활용하여 로그인 기능을 구현하는 것이 매우 간단합니다. 이 글에서는 Firebase을 사용하여 플러터 로그인 로그아웃 기능을 구현하는 과정에 대해 작성합니다.
회원가입 로그인 채탱앱 만들기 추천영상
자세한 설명이 필요하시면 강의영상을 추천드립니다.
*해당 글의 내용과 영상제작 시기가 달라 내용의 차이가 있을 수 있습니다.
플러터 로그인 방법
회원 가입 구현
Flutter 앱에서 Firebase Authentication을 사용하여 안전하고 효율적인 회원가입 시스템을 구현하는 방법은 아래글을 참고하세요.
Firebase 초기화
Flutter 앱에서 Firebase를 사용하기 위해서는 앱이 시작될 때 Firebase를 초기화해야 합니다. main 함수에서 Firebase.initializeApp()를 호출하여 Firebase를 초기화합니다.
import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; import 'package:firebase_auth/firebase_auth.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MyApp()); }
사용자 인증 상태 관리
MyApp 클래스는 앱의 루트 위젯입니다. 여기서 StreamBuilder를 사용하여 Firebase 인증 상태 변경을 실시간으로 감지하고, 사용자의 로그인 상태에 따라 적절한 화면을 표시합니다.
import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( // StreamBuilder를 사용하여 FirebaseAuth의 인증 상태 변경을 감지 home: StreamBuilder<User?>( // FirebaseAuth.instance.authStateChanges()는 사용자 인증 상태가 변경될 때마다 스트림을 통해 알림 stream: FirebaseAuth.instance.authStateChanges(), // 스트림의 데이터(인증 상태 변경)가 업데이트 될 때마다 builder 함수가 호출됩니다. builder: (context, snapshot) { // snapshot.connectionState가 active 상태인 경우, 인증 상태 변경 정보가 준비 if (snapshot.connectionState == ConnectionState.active) { // user 변수에 현재 인증된 사용자 정보를 할당 User? user = snapshot.data; // user가 null인 경우, 사용자가 로그아웃 상태임을 의미하므로 SignInPage()를 반환 if (user == null) { return SignInPage(); } // user가 null이 아닌 경우, 사용자가 로그인 상태임을 의미하므로 HomePage()를 반환 return HomePage(user: user); } // 스트림의 연결 상태가 active 상태가 아닌 경우 로딩 인디케이터를 표시 return Scaffold( body: Center(child: CircularProgressIndicator()), ); }, ), ); } }
로그인 페이지 구현
SignInPage는 사용자가 이메일과 비밀번호를 입력하여 로그인할 수 있는 페이지입니다. 사용자가 로그인 버튼을 누르면 _signIn 메서드가 호출되고, Firebase 인증을 사용하여 로그인을 시도합니다.
class SignInPage extends StatefulWidget { @override _SignInPageState createState() => _SignInPageState(); } class _SignInPageState extends State<SignInPage> { // 사용자 이메일과 비밀번호 입력을 위한 TextEditingController 인스턴스 생성 final _emailController = TextEditingController(); final _passwordController = TextEditingController(); // FirebaseAuth 인스턴스를 사용하여 인증 처리 final _auth = FirebaseAuth.instance; // 사용자에게 로그인 상태 또는 오류 메시지를 표시하기 위한 문자열 변수 String _statusMessage = ''; // 비동기 로그인 처리 메서드 Future<void> _signIn() async { try { // 이메일과 비밀번호를 사용하여 사용자 로그인 시도 final UserCredential userCredential = await _auth.signInWithEmailAndPassword( email: _emailController.text, password: _passwordController.text, ); // 로그인 성공 시 이메일 인증 여부 확인 if (userCredential.user != null) { if (!userCredential.user!.emailVerified) { // 이메일 인증이 완료되지 않은 경우 사용자에게 메시지 표시 setState(() { _statusMessage = '이메일 인증이 완료되지 않았습니다. 이메일을 확인해주세요.'; }); } else { // 이메일 인증이 완료된 경우 로그인 성공 처리 setState(() { _statusMessage = '로그인 성공!'; // 홈 화면으로 이동 Navigator.of(context).push(MaterialPageRoute( builder: (context) => HomePage(user: userCredential.user!))); }); } } } on FirebaseAuthException catch (e) { // FirebaseAuth에서 발생하는 예외 처리 및 오류 메시지 표시 setState(() { _statusMessage = e.message ?? '로그인 실패'; }); } } @override Widget build(BuildContext context) { // MaterialApp과 Scaffold를 사용하여 로그인 페이지 UI 구성 return MaterialApp( home: Scaffold( appBar: AppBar(title: Text("Sign In")), body: SingleChildScrollView( // 스크롤 가능한 컨테이너 내에 로그인 폼 구성 padding: const EdgeInsets.all(16.0), child: Column( children: <Widget>[ // 이메일 입력 필드 Padding( padding: const EdgeInsets.only(bottom: 8.0), child: TextField( controller: _emailController, decoration: InputDecoration(labelText: "Email"), ), ), // 비밀번호 입력 필드 (텍스트 가림 처리) Padding( padding: const EdgeInsets.only(bottom: 20.0), child: TextField( controller: _passwordController, decoration: InputDecoration(labelText: "Password"), obscureText: true, ), ), // 로그인 버튼 ElevatedButton( onPressed: _signIn, child: Text("Sign In"), ), SizedBox(height: 20), // 로그인 상태 또는 오류 메시지 표시 Text(_statusMessage), ], ), ), ), ); } }
로그아웃 페이지 구현
로그인에 성공한 사용자를 위한 HomePage를 구현합니다. 여기서는 사용자의 이메일 주소를 표시하고, 로그아웃 버튼을 누르면 로그인 버튼으로 이동합니다.
_signOut이라는 별도의 비동기 함수를 정의하여 로그아웃 처리를 하였습니다. 이렇게 하면 로그아웃 로직을 재사용할 수 있고, 오류 처리를 위한 로직을 한 곳에 모을 수 있습니다. 만약 로그아웃 과정에서 오류가 발생하면, 사용자에게 적절한 피드백을 제공하기 위해 ScaffoldMessenger를 사용하여 스낵바 메시지를 표시합니다.
class HomePage extends StatelessWidget { // 사용자 정보를 담고 있는 User 객체를 final로 선언 final User user; // 생성자에서 user를 필수 인자로 받음. required 키워드로 null이 아닌 값을 보장 HomePage({required this.user}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("로그인 성공"), actions: [ IconButton( icon: Icon(Icons.logout), // 로그아웃 버튼이 클릭되면 _signOut 함수를 비동기적으로 호출 onPressed: () async { await _signOut(context); }, ), ], ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ // 사용자의 이메일을 화면에 표시 Text("환영합니다, ${user.email}!", style: TextStyle(fontSize: 20)), SizedBox(height: 20), Text("로그인에 성공하였습니다.", style: TextStyle(fontSize: 16)), SizedBox(height: 20), // 로그아웃 버튼을 배치 클릭 시 _signOut 함수를 호출 ElevatedButton( onPressed: () { _signOut(context); }, child: Text("로그아웃"), ), ], ), ), ); } // _signOut 함수는 로그아웃을 처리하는 비동기 함수 Future<void> _signOut(BuildContext context) async { try { // FirebaseAuth의 signOut 메서드를 호출하여 사용자를 로그아웃 await FirebaseAuth.instance.signOut(); // 로그아웃 후 현재 페이지를 종료하고 이전 화면으로 돌아감 Navigator.of(context).pop(); } catch (e) { // 로그아웃 과정에서 오류가 발생한 경우 사용자에게 알림을 제공 ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("로그아웃 중 오류가 발생했습니다."), ), ); } } }
마무리
Flutter와 Firebase를 사용하여 간단한 사용자 인증 시스템을 구축하는 방법을 살펴보았습니다. 이메일과 비밀번호를 사용한 로그인 기능은 앱에 있어 필수적인 부분이며, Firebase 인증을 이용하여 손쉽게 구현하였습니다. 읽어주셔서 감사합니다.