프런트 경험은 거의 전무하다하지만, 평소에 결국 개발자라면 풀스택을 지향해야 한다고 생각하고 있었다.
운이 좋게도 이번에 이직을 진행하면서 풀스택 개발자 포지션으로 이직을 하게되었고, 주로 앱을 개발하다 보니 플러터라는 크로스 플렛폼 프레임워크를 사용하게 되었고, 여유가 있을때 앱을 개발하면서 진행했던 것들에 대한 기록을 남겨보려 한다.
아직은 디자인이 덜 나온 상황이기에 로그인 화면, 빈 홈화면, Signup 화면 이 정도만 구현한 코드에 대해서 정리해 보려고 한다.
파이어베이스를 활용해 구글, 애플, 카카오 로그인을 구현할 생각이며 구글부터 순차적으로 방법에 대해서 작성해보려고 한다.
우선 파이어베이스 계정을 생성하고 내 플러터 프로젝트와 연동을 진행해 주어야 한다.
크로스 플렛폼인만큼 안드로이드와 ios 연동 방법이 다르며 각각 진행을 해줘야하는데, 이 부분은 파이어베이스 홈페이지에 이미 상세하게 나와 있고 코드를 통해서 진행되는 부분은 많지 않기 때문에 나중에 시간이 남는다면 별도로 작성해볼까 한다.
설정을 한 후, 플러터 프로젝트에서 파이어베이스 인스턴스를 initalize해줘야 하는데, 이 부분은 main.dart에서 진행이 가능하다.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'My App',
debugShowCheckedModeBanner: false,
theme: ThemeData.light().copyWith(
scaffoldBackgroundColor: Color(0xFFA1A1A1),
),
home: const LoginScreen(),
);
}
}
해당 main.dart는 간단하다. 파이어베이스를 initialize하고 initialize에 성공하였을 시, 앱의 첫화면인 로그인화면에 보내준다.
이때, 파이어베이스 ios 설정을 제대로 하지 않았으면 ios에서 앱이 구동되지 않으며, 안드로이드쪽에서 설정을 제대로 해주지 않는다면 안드로이드 쪽에서 앱이 구동되지 않기 때문에 주의하도록 한다.
파이어베이스 콘솔에서 authentication 부분에서 우선 구글 로그인 부터 진행할 예정이기 때문에 Signin Method에 구글을 추가해주도록한다.
나는 lib 디렉토리 아래에 authentication라는 디렉토리를 만들어 인증과 관련된 모든 화면 및 함수들을 작성하려고 한다.
내가 만든 로그인 화면은 아래와 같다.
class _LoginScreenState extends State<LoginScreen> {
final Auth authService = Auth();
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 131,
),
Image.asset(
"images/sample-logo.png",
width: 64,
height: 22,
),
const SizedBox(height: 41,),
Image.asset(
"images/xbox.jpeg",
width: 246,
height: 246,
),
const SizedBox(height: 41,),
if (Platform.isIOS) ...[ // This will check if the platform is iOS
Container(
child: SignInWithAppleButton(
onPressed: () async {
await authService.signInWithApple();
},
),
width: 331,
height: 58,
),
],
const SizedBox(height: 15,),
InkWell(
onTap: () async {
await authService.signInWithGoogle();
},
child: Image.asset(
"images/google.png",
width: 331,
height: 58,
fit: BoxFit.cover,
),
),
const SizedBox(height: 15,),
InkWell(
onTap: () async {},
child: Image.asset(
"images/kakao.png",
width: 331,
height: 58,
fit: BoxFit.cover,
),
),
],
),
),
),
);
}
}
애플 로그인은 해당 패키지에서 공식 이미지를 지원해줘서 별도로 설정하지 않아도 되지만, 구글과 카카오 로그인은 이미지를 직접 설정해줘야 한다.
이 프로젝트는 images라는 폴더에 전부 담아 사용할 예정이며 pubspec.yaml에서 assets라고 커멘트 처리가 된 부분이 있는데 이를 다음과 같이 설정하면 된다.
# To add assets to your application, add an assets section, like this:
assets:
- images/
이렇게 설정해주게 된다면 images폴더 안에 있는 이미지를 플러터 프로젝트에서 사용이 가능해진다.
그리고 애플, 구글, 카카오 로그인을 위해서 패키지들을 설치해줘야 할 것들이 있는데 필요한 dependencies들을 pubspec.yaml에 추가해준다.
sign_in_with_apple: ^4.3.0
firebase_auth: ^4.6.3
firebase_core: ^2.14.0
google_sign_in: ^6.1.4
kakao_flutter_sdk: ^1.4.3
이렇게 설정을 한다면 안드로이드 화면은 아래와 같이 나오며
아이폰 화면은 시스템에서 ios라 인식하여 애플 로그인과 같이 아래와 같이 나오게 된다.
해당 버튼을 눌렀을때 로그인을 하는 방법을 구현해야 한다.
이는 별도의 auth.dart에 구현하였으며, 로그인 화면의 constructer에 포함시켰다.
내가 구현한 매써드는 아래와 같다
Future<User?> signInWithGoogle() async {
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
if (googleUser == null) {
return null;
}
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final oauthCredential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
final userCredential = await _firebaseAuth.signInWithCredential(oauthCredential);
final user = userCredential.user;
// Optionally get FCM token for push notifications
FirebaseMessaging messaging = FirebaseMessaging.instance;
String? token = await messaging.getToken();
await _storeUserInfoInPrefs(user, token);
int userCheck = await this.isRegistered(user!.email!);
if(userCheck != 0){
this.updateMessagingToken(userCheck, token!);
Get.to(HomeScreen());
}else{
Get.to(SignupScreen());
}
return user;
}
코드에 대해서 설명을 하자면 GoogleSignIn을 호출하고, 그에 맞는 accessToken과 idToken을 갖고 온다.
해당 토큰을 이용해 firebase의 인증을 진행을하고, 파이어베이스에서 인증 된 정보를 앱에서 계속 사용할 것이기 때문에 sharedPreferences에 담아준다.
필자는 파이어베이스의 푸시알림 서비스인 Firebase Cloud Messaging 서비스 역시 사용할 것이기 때문에 이에 해당하는 토큰도 발급 받아 sharedPreferences에 담아주었다.
그 후 해당 유저의 이메일 정보를 갖고 와 이미 존재하는 유저면 FCM을 위한 토큰을 유저 db에 업데이트 하는 updateMessagingToken 함수를 호출한 후 홈 화면으로, 그렇지 않다면 추가적인 정보를 받는 SignupScreen으로 전달을 해준다.
유저가 존재하는지 여부를 확인하는 isRegistered 함수는 아래와 같은데 간단하다.
Future<int> isRegistered(String email) async{
final response = await http.post(
Uri.parse('$baseUrl/user/email?email=$email'),
);
if (response.statusCode == 201 && response.body.length>0) {
Map<String, dynamic> jsonResponse = jsonDecode(response.body);
return jsonResponse['id'];
} else {
return 0;
}
백엔드쪽에 이메일을 갖고 db를 탐색하는 함수이며 찾을 시 해당 유저의 id 아닐 시 0을 반환하는 함수다.
해당 방법으로 db에 이미 이메일이 존재하지 않는 유저로 구글 로그인을 진행하면 SignupScreen에, 그렇지 않은 경우는 홈 스크린에 랜딩이 되는 것을 확인 할 수 있다.