wan의 개발일기
[Flutter] 1. Firebase Auth로 이메일 로그인/회원가입 만들기 본문
들어가며
최근에 Flutter와 Firebase를 조합해서 실시간 채팅 앱을 만들어보고 있다. 평소 Java/Spring이나 Node.js로 백엔드를 직접 짜는 데 익숙했는데, Firebase를 쓰면 인증 서버를 따로 구축하지 않고도 "회원가입 - 로그인" 흐름을 빠르게 붙일 수 있어서 학습용으로 잡기 좋았다.
이번 글에서는 시리즈의 첫 단계, 즉 Firebase 프로젝트 연동 + 이메일/비밀번호 인증의 최초 구현만 다룬다. 비밀번호 재설정이나 자동 로그인 유지처럼 나중에 추가된 기능은 이후 편에서 그 기능이 실제로 들어간 시점에 맞춰 다룰 예정이다.
프로젝트 한눈에 보기
이 시리즈에서 만드는 앱은 Flutter + Firebase로 구현한 실시간 채팅 애플리케이션이다. 최종적으로는 1:1 / 그룹 채팅, 이미지 전송, 읽음 표시, 타이핑 인디케이터, 온라인 상태 표시까지 들어가지만, 가장 첫 커밋에서는 딱 두 가지만 있었다 — Firebase 연동과 이메일 로그인/회원가입.
| 패키지 | 역할 |
|---|---|
| firebase_core | Firebase 앱 초기화 |
| firebase_auth | 이메일/비밀번호 인증 |
Firebase 초기화
Java 백엔드에서 Spring Context를 띄우는 것처럼, Flutter 앱도 main()에서 Firebase SDK를 가장 먼저 초기화해야 한다. WidgetsFlutterBinding.ensureInitialized()는 Flutter 엔진과 네이티브 플랫폼 채널을 연결해주는 호출인데, 비동기 초기화(Firebase.initializeApp)를 runApp() 전에 await하려면 반드시 필요하다.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// FlutterFire CLI가 생성한 firebase_options.dart의 플랫폼별 설정값 사용
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: 'Chat App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
// 최초 버전에서는 로그인 여부 판단 없이 곧장 로그인 화면으로
home: const LoginScreen(),
);
}
}
firebase_options.dart는 직접 작성하지 않고 flutterfire configure CLI로 생성한다. Android/iOS/Web마다 API 키와 프로젝트 설정이 다르기 때문에, 플랫폼을 감지해서 맞는 옵션을 골라주는 역할을 한다.
로그인 / 회원가입 화면
LoginScreen은 하나의 화면에서 _isLogin 불리언 플래그로 로그인 모드와 회원가입 모드를 토글한다. JS 프론트엔드에서 폼 하나를 상태값으로 로그인/가입 뷰 전환하는 패턴과 동일하다.
Future<void> _submit() async {
final email = _emailController.text.trim();
final password = _passwordController.text.trim();
if (email.isEmpty || password.isEmpty) {
setState(() => _errorMessage = '이메일과 비밀번호를 입력해주세요.');
return;
}
try {
if (_isLogin) {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
} else {
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email,
password: password,
);
}
if (mounted) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const HomeScreen()),
);
}
} on FirebaseAuthException catch (e) {
// 잘못된 비밀번호, 이미 가입된 이메일 등 Firebase가 메시지를 내려준다
setState(() => _errorMessage = e.message ?? '오류가 발생했습니다.');
}
}
이 시점의
_submit()은 정말 딱 인증만 한다. 로그인/가입에 성공하면Navigator.pushReplacement로 홈 화면으로 직접 이동시키는 식이다. Firestore에 유저 문서를 저장하거나, 앱을 재시작해도 로그인이 유지되는 건 아직 없다 — 채팅 기능을 만들면서 "유저를 검색하려면 DB에 저장된 정보가 필요하다"는 걸 깨닫고 나서야 추가했다. Java로 치면 인증(Authentication)과 사용자 프로필(User 엔티티)을 처음부터 분리 설계한 게 아니라, 필요해질 때 뒤늦게 나눈 셈이다.
에러 처리
FirebaseAuthException은 e.message에 사람이 읽을 수 있는 영문 에러 메시지를 담아준다. Java의 체크 예외처럼 어떤 예외가 던져질지 미리 알 수 있는 건 아니지만, code 필드(예: email-already-in-use, wrong-password)로 분기해서 한글 메시지로 바꿔줄 수도 있다. 이 프로젝트에서는 일단 Firebase가 주는 메시지를 그대로 화면에 띄우는 선에서 시작했다.
정리
| 기능 | 핵심 API |
|---|---|
| Firebase 초기화 | Firebase.initializeApp() |
| 회원가입 | createUserWithEmailAndPassword() |
| 로그인 | signInWithEmailAndPassword() |
| 에러 처리 | FirebaseAuthException |
다음 글에서는 이 인증 위에서 Firestore StreamBuilder로 1:1 실시간 채팅을 어떻게 구현했는지, 그리고 회원가입 시 Firestore에 유저 문서를 함께 저장하게 된 이유를 다뤄볼 예정이다.
'Flutter' 카테고리의 다른 글
| [Flutter] 6. 닉네임/이메일 prefix 검색 + 온라인 상태 표시 (0) | 2026.06.24 |
|---|---|
| [Flutter] 5. 읽음 표시 + 타이핑 인디케이터로 채팅 디테일 살리기 (0) | 2026.06.24 |
| [Flutter] 4. 프로필(닉네임/사진) 만들고 채팅 이미지까지 전송하기 (0) | 2026.06.24 |
| [Flutter] 3. 그룹 채팅 추가하기 + authStateChanges로 로그인 유지 (0) | 2026.06.24 |
| [Flutter] 2. Firestore StreamBuilder로 1:1 실시간 채팅 구현하기 (0) | 2026.06.24 |