플러터:인증(Firebase 인증): 두 판 사이의 차이
보이기
새 문서: {{플러터}} = 개요 = Flutter에서 Firebase Authentication을 이용해 앱에 로그인 기능을 구현하는 방법을 정리한 문서. === 특징 === * 쉽다: Firebase는 이메일/비밀번호, 구글, 애플, 전화번호 등 다양한 인증 방식을 제공하며, 별도의 백엔드 없이도 안정적인 인증 시스템을 구축할 수 있다. * Firebase가 제공하는 인증 서비스(공식지원) * 서버 없이 사용자 계정 관리 가능(백엔드... |
|||
| 26번째 줄: | 26번째 줄: | ||
# authStateChanges() 스트림을 통한 로그인 상태 전달 | # authStateChanges() 스트림을 통한 로그인 상태 전달 | ||
= 사전 준비 = | = 기본 인증 (Email/Password) = | ||
이메일과 패스워드만 사용한 인증을 구현한다.(그런데, 사실 소셜로그인을 이용하는 경우가 많이 때문에.. 이 방식은 굳이...?) | |||
=== 사전 준비 === | |||
{| class="wikitable" | {| class="wikitable" | ||
|+ | |+ | ||
| 34번째 줄: | 37번째 줄: | ||
|- | |- | ||
|Firebase CLI 설치 | |Firebase CLI 설치 | ||
+ 로그인 | |||
| | | | ||
* npm install -g firebase-tools | |||
* firebase login | |||
|nodeJS 설치되어 있어야 함. 없다면 https://nodejs.org/<nowiki/>참고. | |||
firebase --version 에서 숫자가 나오면 정상. | |||
|- | |- | ||
|FlutterFire CLI 설치 | |FlutterFire CLI 설치 | ||
| | |dart pub global activate flutterfire_cli | ||
dart pub global activate flutterfire_cli | |||
| | | | ||
|- | |- | ||
|Firebase 프로젝트 연동 | |Firebase 프로젝트 연동 | ||
|<syntaxhighlight lang=" | |flutterfire configure | ||
→ Android/iOS 설정 파일 자동 생성 | |||
</syntaxhighlight> | |파이어베이스에서 작성해둔 프로젝트 중 하나를 고르게 된다. 여기에 맞게 설정 파일을 자동으로 생성. | ||
|- | |||
|pubspec.yaml 설정 | |||
|<syntaxhighlight lang="yaml">dependencies: | |||
firebase_core: ^3.8.1 | |||
firebase_auth: ^5.3.3</syntaxhighlight> | |||
| | | | ||
|- | |- | ||
| | |패키지 설치 | ||
| | |flutter pub get | ||
| | | | ||
|} | |} | ||
== main.dart == | == main.dart == | ||
| 107번째 줄: | 108번째 줄: | ||
= 구글 로그인 = | = 구글 로그인 = | ||
안타깝게도 Window 앱에선 지원하지 않는다. 기본적으로 플러터는 모바일과 웹을 위한 것. | |||
=== 사전 준비 === | |||
{| class="wikitable" | |||
|+ | |||
!단계 | |||
!설명 | |||
!비고 | |||
|- | |||
|Firebase 콘솔 설정 | |||
|파이어베이스 콘솔에서 제공업체를 선택한다. | |||
Authentication → Sign-in Method → Google 활성화 | |||
로그인 방법 > 새 제공업체 등에서 선택한다. | |||
|https://console.firebase.google.com/project/ktalk-d20ab/authentication/providers | |||
|- | |||
|(웹의 경우) | |||
구글 클라우드 설정 | |||
|클라우드 콘솔 설정.(반드시 파이어베이스와 같은 프로젝트로 진행해야 한다.) | |||
* https://console.cloud.google.com 접속 | |||
* Firebase 프로젝트와 연결된 프로젝트 선택 | |||
* | * 왼쪽 메뉴: API 및 서비스 → OAuth 동의 화면, 외부 선택 → 만들기 | ||
* 앱 이름, 이메일 입력 → 저장 | |||
* 왼쪽 메뉴: API 및 서비스 → 사용자 인증 정보 | |||
* 상단: + 사용자 인증 정보 만들기 → OAuth 2.0 클라이언트 ID | |||
* 애플리케이션 유형: 웹 애플리케이션 | |||
* 이름: <code>Web Client</code> (아무거나) | |||
* 만들기 클릭 | |||
* 생성되면 팝업에서 클라이언트 ID 복사 (xxx.apps.googleusercontent.com 형식) | |||
index.html에 추가 | |||
<meta name="google-signin-client_id" content="여기에_클라이언트_ID_붙여넣기.apps.googleusercontent.com"> 형식으로. | |||
아래 링크에서 People API를 활성화해준다.(프론트에서 로그인을 처리하기에 과정이 좀 있네;;) | |||
* 또는 Google Cloud Console → '''API 및 서비스''' → '''라이브러리''' | |||
| | |||
* 안드로이드의 경우 거의 작업 없이 진행됨. | |||
|- | |||
|Firebase CLI 설치 | |||
+ 로그인 | |||
| | |||
* npm install -g firebase-tools | |||
* firebase login | |||
|nodeJS 설치되어 있어야 함. 없다면 https://nodejs.org/<nowiki/>참고. | |||
firebase --version 에서 숫자가 나오면 정상. | |||
|- | |||
|FlutterFire CLI 설치 | |||
|dart pub global activate flutterfire_cli | |||
| | |||
|- | |||
|Firebase 프로젝트 연동 | |||
|flutterfire configure | |||
→ Android/iOS 설정 파일 자동 생성 | |||
| | |||
* 파이어베이스에서 작성해둔 프로젝트 중 하나를 고르게 된다. 여기에 맞게 설정 파일을 자동으로 생성. | |||
* 콘솔에서 제공업체에 변경이 발생할 때마다 다시 진행해야 한다. | |||
|- | |||
|pubspec.yaml 설정 | |||
|<syntaxhighlight lang="yaml">dependencies: | |||
firebase_core: ^3.8.1 | |||
firebase_auth: ^5.3.3 | |||
google_sign_in: ^6.2.2 # 공급업체에 따라 추가해준다.</syntaxhighlight> | |||
| | |||
|- | |||
|패키지 설치 | |||
|flutter pub get | |||
| | |||
|} | |||
== 구현 코드 == | == 구현 코드 == | ||
<syntaxhighlight lang="dart"> | <syntaxhighlight lang="dart"> | ||
import 'package:flutter/material.dart'; | |||
import 'package:firebase_core/firebase_core.dart'; | |||
import 'package:firebase_auth/firebase_auth.dart'; | |||
import 'package:google_sign_in/google_sign_in.dart'; | |||
import 'firebase_options.dart'; | |||
// 앱 시작 전에 Firebase를 초기화해야 함 | |||
void main() async { | |||
// Flutter 엔진이 완전히 초기화되도록 보장 (Firebase 사용 전 필수) | |||
WidgetsFlutterBinding.ensureInitialized(); | |||
// Firebase 초기화 (firebase_options.dart 파일 사용) await로 초기화 완료될 때까지 기다려야 함 | |||
await Firebase.initializeApp( | |||
options: DefaultFirebaseOptions.currentPlatform, | |||
); | ); | ||
runApp(const MyApp()); | |||
} | |||
class MyApp extends StatelessWidget { | |||
const MyApp({super.key}); | |||
@override | |||
Widget build(BuildContext context) { | |||
return MaterialApp( | |||
title: 'Google 로그인 학습', | |||
theme: ThemeData( | |||
primarySwatch: Colors.blue, | |||
), | |||
home: const GoogleSignInPage(), | |||
); | |||
} | |||
} | |||
class GoogleSignInPage extends StatefulWidget { | |||
const GoogleSignInPage({super.key}); | |||
@override | |||
State<GoogleSignInPage> createState() => _GoogleSignInPageState(); | |||
} | } | ||
== 구글 로그인 | class _GoogleSignInPageState extends State<GoogleSignInPage> { | ||
// Firebase Authentication 인스턴스 (사용자 인증 관리) | |||
final FirebaseAuth _auth = FirebaseAuth.instance; | |||
// Google Sign-In 인스턴스 (구글 로그인 처리) | |||
) | final GoogleSignIn _googleSignIn = GoogleSignIn(); | ||
</ | |||
// 현재 로그인된 사용자 정보를 저장할 변수 | |||
User? _user; | |||
@override | |||
void initState() { | |||
super.initState(); | |||
// 앱 시작시 현재 로그인 상태 확인 | |||
_user = _auth.currentUser; | |||
} | |||
// 구글 로그인 함수 | |||
Future<void> _signInWithGoogle() async { | |||
try { | |||
// 1. Google Sign-In 팝업 띄우기 (사용자가 구글 계정 선택) | |||
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn(); | |||
// 사용자가 로그인 취소한 경우 | |||
if (googleUser == null) { | |||
print('로그인 취소됨'); | |||
return; | |||
} | |||
// 2. 선택한 구글 계정의 인증 정보 가져오기 | |||
final GoogleSignInAuthentication googleAuth = | |||
await googleUser.authentication; | |||
// 3. Firebase용 인증 자격증명 생성 | |||
// accessToken: 구글 API 접근용 토큰 | |||
// idToken: 사용자 신원 확인용 토큰 | |||
final credential = GoogleAuthProvider.credential( | |||
accessToken: googleAuth.accessToken, | |||
idToken: googleAuth.idToken, | |||
); | |||
// 4. Firebase에 자격증명으로 로그인 | |||
final UserCredential userCredential = | |||
await _auth.signInWithCredential(credential); | |||
// 5. 로그인 성공 - UI 업데이트 | |||
setState(() { | |||
_user = userCredential.user; | |||
}); | |||
print('로그인 성공: ${_user?.displayName}'); | |||
} catch (e) { | |||
// 로그인 실패시 에러 출력 | |||
print('로그인 실패: $e'); | |||
// 사용자에게 에러 메시지 표시 | |||
if (mounted) { | |||
ScaffoldMessenger.of(context).showSnackBar( | |||
SnackBar(content: Text('로그인 실패: $e')), | |||
); | |||
} | |||
} | |||
} | |||
// 로그아웃 함수 | |||
Future<void> _signOut() async { | |||
try { | |||
// Firebase에서 로그아웃 | |||
await _auth.signOut(); | |||
// Google Sign-In에서도 로그아웃 (다음 로그인시 계정 선택 가능) | |||
await _googleSignIn.signOut(); | |||
// UI 업데이트 | |||
setState(() { | |||
_user = null; | |||
}); | |||
print('로그아웃 성공'); | |||
} catch (e) { | |||
print('로그아웃 실패: $e'); | |||
} | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
return Scaffold( | |||
appBar: AppBar( | |||
title: const Text('구글 로그인 학습'), | |||
centerTitle: true, | |||
), | |||
body: Center( | |||
child: Padding( | |||
padding: const EdgeInsets.all(20.0), | |||
child: _user == null | |||
? _buildSignInUI() // 로그인 안된 상태 UI | |||
: _buildUserInfoUI(), // 로그인된 상태 UI | |||
), | |||
), | |||
); | |||
} | |||
// 로그인 버튼 UI (로그인 전) | |||
Widget _buildSignInUI() { | |||
return Column( | |||
mainAxisAlignment: MainAxisAlignment.center, | |||
children: [ | |||
const Icon( | |||
Icons.account_circle, | |||
size: 100, | |||
color: Colors.grey, | |||
), | |||
const SizedBox(height: 30), | |||
const Text( | |||
'구글 계정으로 로그인하세요', | |||
style: TextStyle(fontSize: 18), | |||
), | |||
const SizedBox(height: 30), | |||
// 구글 로그인 버튼 | |||
ElevatedButton.icon( | |||
onPressed: _signInWithGoogle, | |||
icon: const Icon(Icons.login), | |||
label: const Text('Google로 로그인'), | |||
style: ElevatedButton.styleFrom( | |||
padding: const EdgeInsets.symmetric( | |||
horizontal: 30, | |||
vertical: 15, | |||
), | |||
textStyle: const TextStyle(fontSize: 16), | |||
), | |||
), | |||
], | |||
); | |||
} | |||
// 사용자 정보 UI (로그인 후) | |||
Widget _buildUserInfoUI() { | |||
return Column( | |||
mainAxisAlignment: MainAxisAlignment.center, | |||
children: [ | |||
// 프로필 사진 (없으면 기본 아이콘) | |||
_user?.photoURL != null | |||
? CircleAvatar( | |||
radius: 50, | |||
backgroundImage: NetworkImage(_user!.photoURL!), | |||
) | |||
: const CircleAvatar( | |||
radius: 50, | |||
child: Icon(Icons.person, size: 50), | |||
), | |||
const SizedBox(height: 20), | |||
// 사용자 이름 | |||
Text( | |||
_user?.displayName ?? '이름 없음', | |||
style: const TextStyle( | |||
fontSize: 24, | |||
fontWeight: FontWeight.bold, | |||
), | |||
), | |||
const SizedBox(height: 10), | |||
// 이메일 | |||
Text( | |||
_user?.email ?? '이메일 없음', | |||
style: const TextStyle( | |||
fontSize: 16, | |||
color: Colors.grey, | |||
), | |||
), | |||
const SizedBox(height: 10), | |||
// UID (Firebase에서 부여한 고유 ID) | |||
Text( | |||
'UID: ${_user?.uid}', | |||
style: const TextStyle( | |||
fontSize: 12, | |||
color: Colors.grey, | |||
), | |||
textAlign: TextAlign.center, | |||
), | |||
const SizedBox(height: 30), | |||
// 로그아웃 버튼 | |||
ElevatedButton.icon( | |||
onPressed: _signOut, | |||
icon: const Icon(Icons.logout), | |||
label: const Text('로그아웃'), | |||
style: ElevatedButton.styleFrom( | |||
backgroundColor: Colors.red, | |||
foregroundColor: Colors.white, | |||
padding: const EdgeInsets.symmetric( | |||
horizontal: 30, | |||
vertical: 15, | |||
), | |||
textStyle: const TextStyle(fontSize: 16), | |||
), | |||
), | |||
], | |||
); | |||
} | |||
} | |||
</syntaxhighlight> | |||
* | * | ||
2025년 12월 2일 (화) 01:16 판
- 플러터:개요
- 플러터:실행
- 플러터:개념 잡기
- 권한 사용
- 위젯
- 플러터:DB연결
- 플러터:Firebase(미완)
- 플러터:MySQL(미완)
- 디자인
- 플러터:배포
- 플러터:배포(안드로이드)(미완)
- 플러터:배포(iOS)(미완)
- 플러터:참고자료
- 플러터:위젯
- 플러터:구글 AdMob(미완)
- 플러터:라이브러리
개요
Flutter에서 Firebase Authentication을 이용해 앱에 로그인 기능을 구현하는 방법을 정리한 문서.
특징
- 쉽다: Firebase는 이메일/비밀번호, 구글, 애플, 전화번호 등 다양한 인증 방식을 제공하며, 별도의 백엔드 없이도 안정적인 인증 시스템을 구축할 수 있다.
- Firebase가 제공하는 인증 서비스(공식지원)
- 서버 없이 사용자 계정 관리 가능(백엔드 필요 없음)
- 소셜 로그인(Google/Apple/GitHub/Microsoft) 지원
- 인증 상태(로그인 여부) 자동 유지
- 토큰/세션 관리 자동 처리
한계
- 기본적으로 제공하는 소셜로그인 외의 OAuth2 등 인증은 따로 구현하는 편이 더 간편하다.
Firebase 인증 개념
인증 흐름
- 앱에서 FirebaseAuth API 호출
- Firebase가 사용자 인증 처리
- 성공 시 User 객체 반환
- authStateChanges() 스트림을 통한 로그인 상태 전달
기본 인증 (Email/Password)
이메일과 패스워드만 사용한 인증을 구현한다.(그런데, 사실 소셜로그인을 이용하는 경우가 많이 때문에.. 이 방식은 굳이...?)
사전 준비
| 단계 | 설명 | 비고 |
|---|---|---|
| Firebase CLI 설치
+ 로그인 |
|
nodeJS 설치되어 있어야 함. 없다면 https://nodejs.org/참고.
firebase --version 에서 숫자가 나오면 정상. |
| FlutterFire CLI 설치 | dart pub global activate flutterfire_cli | |
| Firebase 프로젝트 연동 | flutterfire configure
→ Android/iOS 설정 파일 자동 생성 |
파이어베이스에서 작성해둔 프로젝트 중 하나를 고르게 된다. 여기에 맞게 설정 파일을 자동으로 생성. |
| pubspec.yaml 설정 | dependencies:
firebase_core: ^3.8.1
firebase_auth: ^5.3.3
|
|
| 패키지 설치 | flutter pub get |
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
회원가입
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email,
password: password,
);
로그인
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
로그아웃
await FirebaseAuth.instance.signOut();
로그인 상태 감지
FirebaseAuth는 로그인 상태를 Stream으로 감시할 수 있다.
StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return HomeScreen(); // 로그인됨
}
return LoginScreen(); // 로그인 안됨
},
)
구글 로그인
안타깝게도 Window 앱에선 지원하지 않는다. 기본적으로 플러터는 모바일과 웹을 위한 것.
사전 준비
| 단계 | 설명 | 비고 |
|---|---|---|
| Firebase 콘솔 설정 | 파이어베이스 콘솔에서 제공업체를 선택한다.
Authentication → Sign-in Method → Google 활성화 로그인 방법 > 새 제공업체 등에서 선택한다. |
https://console.firebase.google.com/project/ktalk-d20ab/authentication/providers |
| (웹의 경우)
구글 클라우드 설정 |
클라우드 콘솔 설정.(반드시 파이어베이스와 같은 프로젝트로 진행해야 한다.)
index.html에 추가 <meta name="google-signin-client_id" content="여기에_클라이언트_ID_붙여넣기.apps.googleusercontent.com"> 형식으로.
아래 링크에서 People API를 활성화해준다.(프론트에서 로그인을 처리하기에 과정이 좀 있네;;)
|
|
| Firebase CLI 설치
+ 로그인 |
|
nodeJS 설치되어 있어야 함. 없다면 https://nodejs.org/참고.
firebase --version 에서 숫자가 나오면 정상. |
| FlutterFire CLI 설치 | dart pub global activate flutterfire_cli | |
| Firebase 프로젝트 연동 | flutterfire configure
→ Android/iOS 설정 파일 자동 생성 |
|
| pubspec.yaml 설정 | dependencies:
firebase_core: ^3.8.1
firebase_auth: ^5.3.3
google_sign_in: ^6.2.2 # 공급업체에 따라 추가해준다.
|
|
| 패키지 설치 | flutter pub get |
구현 코드
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'firebase_options.dart';
// 앱 시작 전에 Firebase를 초기화해야 함
void main() async {
// Flutter 엔진이 완전히 초기화되도록 보장 (Firebase 사용 전 필수)
WidgetsFlutterBinding.ensureInitialized();
// Firebase 초기화 (firebase_options.dart 파일 사용) await로 초기화 완료될 때까지 기다려야 함
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Google 로그인 학습',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const GoogleSignInPage(),
);
}
}
class GoogleSignInPage extends StatefulWidget {
const GoogleSignInPage({super.key});
@override
State<GoogleSignInPage> createState() => _GoogleSignInPageState();
}
class _GoogleSignInPageState extends State<GoogleSignInPage> {
// Firebase Authentication 인스턴스 (사용자 인증 관리)
final FirebaseAuth _auth = FirebaseAuth.instance;
// Google Sign-In 인스턴스 (구글 로그인 처리)
final GoogleSignIn _googleSignIn = GoogleSignIn();
// 현재 로그인된 사용자 정보를 저장할 변수
User? _user;
@override
void initState() {
super.initState();
// 앱 시작시 현재 로그인 상태 확인
_user = _auth.currentUser;
}
// 구글 로그인 함수
Future<void> _signInWithGoogle() async {
try {
// 1. Google Sign-In 팝업 띄우기 (사용자가 구글 계정 선택)
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
// 사용자가 로그인 취소한 경우
if (googleUser == null) {
print('로그인 취소됨');
return;
}
// 2. 선택한 구글 계정의 인증 정보 가져오기
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
// 3. Firebase용 인증 자격증명 생성
// accessToken: 구글 API 접근용 토큰
// idToken: 사용자 신원 확인용 토큰
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
// 4. Firebase에 자격증명으로 로그인
final UserCredential userCredential =
await _auth.signInWithCredential(credential);
// 5. 로그인 성공 - UI 업데이트
setState(() {
_user = userCredential.user;
});
print('로그인 성공: ${_user?.displayName}');
} catch (e) {
// 로그인 실패시 에러 출력
print('로그인 실패: $e');
// 사용자에게 에러 메시지 표시
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('로그인 실패: $e')),
);
}
}
}
// 로그아웃 함수
Future<void> _signOut() async {
try {
// Firebase에서 로그아웃
await _auth.signOut();
// Google Sign-In에서도 로그아웃 (다음 로그인시 계정 선택 가능)
await _googleSignIn.signOut();
// UI 업데이트
setState(() {
_user = null;
});
print('로그아웃 성공');
} catch (e) {
print('로그아웃 실패: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('구글 로그인 학습'),
centerTitle: true,
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: _user == null
? _buildSignInUI() // 로그인 안된 상태 UI
: _buildUserInfoUI(), // 로그인된 상태 UI
),
),
);
}
// 로그인 버튼 UI (로그인 전)
Widget _buildSignInUI() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.account_circle,
size: 100,
color: Colors.grey,
),
const SizedBox(height: 30),
const Text(
'구글 계정으로 로그인하세요',
style: TextStyle(fontSize: 18),
),
const SizedBox(height: 30),
// 구글 로그인 버튼
ElevatedButton.icon(
onPressed: _signInWithGoogle,
icon: const Icon(Icons.login),
label: const Text('Google로 로그인'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 30,
vertical: 15,
),
textStyle: const TextStyle(fontSize: 16),
),
),
],
);
}
// 사용자 정보 UI (로그인 후)
Widget _buildUserInfoUI() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 프로필 사진 (없으면 기본 아이콘)
_user?.photoURL != null
? CircleAvatar(
radius: 50,
backgroundImage: NetworkImage(_user!.photoURL!),
)
: const CircleAvatar(
radius: 50,
child: Icon(Icons.person, size: 50),
),
const SizedBox(height: 20),
// 사용자 이름
Text(
_user?.displayName ?? '이름 없음',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
// 이메일
Text(
_user?.email ?? '이메일 없음',
style: const TextStyle(
fontSize: 16,
color: Colors.grey,
),
),
const SizedBox(height: 10),
// UID (Firebase에서 부여한 고유 ID)
Text(
'UID: ${_user?.uid}',
style: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 30),
// 로그아웃 버튼
ElevatedButton.icon(
onPressed: _signOut,
icon: const Icon(Icons.logout),
label: const Text('로그아웃'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: 30,
vertical: 15,
),
textStyle: const TextStyle(fontSize: 16),
),
),
],
);
}
}