[React Native] Expo Android iOS 구글 로그인 성공하기 without firebase

개발 환경 > Firebase 사용 안함 Expo 사용 React Native 0.77 이상 @react-native-google-signin/google-signin 라이브러리 사용 들어가기 전에 RN EXPO로 어플 만들다보면, AI는 공식문서에 있는 라이브러리

Frontend·

개발 환경

Firebase 사용 안함 Expo 사용 React Native 0.77 이상 @react-native-google-signin/google-signin 라이브러리 사용

들어가기 전에

RN EXPO로 어플 만들다보면, AI는 공식문서에 있는 라이브러리로 코드를 알려주는데 그러다보면 auth-session으로 알려준다

또는 구글링 하다보면 auth-session으로 구글 로그인을 진행한 사람이 있는데, 이건 옛날 게시물일 경우가 허다함

[React Native] Expo Android iOS 구글 로그인 성공하기 without firebase

이거 보면 " You cannot use Google auth-session in EXPO Go anymore "

처음 auth-session 으로 진행 하던 중 redirect_uri_match 오류가 계속 생겨서 5시간 동안 삽질하다가 같은 오류의 expo 이슈를 발견하게 되었다

더 이상 Google auth-session은 EXPO Go 에서 사용할 수 없다고 되어있음

따라서 다시 정신을 부여잡고

[React Native] Expo Android iOS 구글 로그인 성공하기 without firebase Goggle-siginin

사용하기 로 함

Google Cloud

https://console.cloud.google.com/ 우선 여기서 프로젝트 생성을 해야함

만드는 방법은 Web Client랑 iOS Client 만들면 되는데 이건 구글링하면 금방나오니 패스

Web Client

승인된 JavaScript 원본 : https://auth.expo.io

승인된 리디렉션 URI : https://auth.expo.io/{@expo에 등록된 나(whoami 명령어 있음)}/{slug}

혹시 모르니 서버 리다이렉트 uri도 넣는거 추천!

iOS Client

번들 Id는 : app.json 보면 bundleIdentifier이나, package 보면 com.xxxxx.xxxx 이렇게 되어 있는게 있는데 그거 넣으면 된다 !

모든 설정이 끝났으면

... 생략
// ++ 이 부분 추가하세요.
GoogleSignin.configure({
webClientId: GOOGLE_CLIENT_ID_WEB,
iosClientId: GOOGLE_CLIENT_ID_IOS,
scopes: ['email', 'profile'],
offlineAccess: false,
forceCodeForRefreshToken: false,
});

android는 GoogleSignIn에서 webClientId로 받아온다

[React Native] Expo Android iOS 구글 로그인 성공하기 without firebase

만약에 지금까지 그냥 npx expo start로 실행했다면, 이제 안될거다 GoogleSignIn은 expo를 지원 안하기 때문에 development build 해야함

expo로 개발하면 환경설정 많이 안할 줄 알았는데 결국 하게 되는 현실

development build

# 먼저 개발 빌드 생성
npx expo install expo-dev-client
# iOS development build 생성
eas build --profile development --platform ios
# Android development build 생성
eas build --profile development --platform Android

Google Sign-In 코드 예제

// hooks/useGoogleAuth.ts
import { useState, useEffect } from 'react';
import { GoogleSignin, statusCodes } from '@react-native-google-signin/google-signin';
interface GoogleUser {
id: string;
name: string;
email: string;
photo?: string;
}
export const useGoogleAuth = () => {
const [user, setUser] = useState<GoogleUser | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
// Google Sign-In 초기 설정
GoogleSignin.configure({
// iOS 클라이언트 ID
iosClientId: '584178392628-loms8bh6egsfr1q2fl9om43i9i51530g.apps.googleusercontent.com',
// 웹 클라이언트 ID (Firebase 콘솔에서 확인)
webClientId: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com',
// 추가 설정
offlineAccess: true,
hostedDomain: '',
forceCodeForRefreshToken: true,
});
// 현재 로그인 상태 확인
checkCurrentUser();
}, []);
const checkCurrentUser = async () => {
try {
const isSignedIn = await GoogleSignin.isSignedIn();
if (isSignedIn) {
const userInfo = await GoogleSignin.getCurrentUser();
if (userInfo?.user) {
setUser({
id: userInfo.user.id,
name: userInfo.user.name || '',
email: userInfo.user.email,
photo: userInfo.user.photo || undefined,
});
}
}
} catch (error) {
console.error('현재 사용자 확인 실패:', error);
}
};
const signIn = async () => {
try {
setLoading(true);
setError(null);
// Google Play Services 확인 (Android)
await GoogleSignin.hasPlayServices();
// 로그인 실행
const userInfo = await GoogleSignin.signIn();
if (userInfo?.user) {
const googleUser: GoogleUser = {
id: userInfo.user.id,
name: userInfo.user.name || '',
email: userInfo.user.email,
photo: userInfo.user.photo || undefined,
};
setUser(googleUser);
return googleUser;
}
} catch (error: any) {
console.error('Google 로그인 실패:', error);
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
setError('로그인이 취소되었습니다.');
} else if (error.code === statusCodes.IN_PROGRESS) {
setError('로그인이 진행 중입니다.');
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
setError('Google Play Services를 사용할 수 없습니다.');
} else {
setError('로그인 중 오류가 발생했습니다.');
}
} finally {
setLoading(false);
}
};
const signOut = async () => {
try {
setLoading(true);
await GoogleSignin.signOut();
setUser(null);
} catch (error) {
console.error('로그아웃 실패:', error);
setError('로그아웃 중 오류가 발생했습니다.');
} finally {
setLoading(false);
}
};
const revokeAccess = async () => {
try {
setLoading(true);
await GoogleSignin.revokeAccess();
setUser(null);
} catch (error) {
console.error('액세스 철회 실패:', error);
setError('액세스 철회 중 오류가 발생했습니다.');
} finally {
setLoading(false);
}
};
return {
user,
loading,
error,
signIn,
signOut,
revokeAccess,
isSignedIn: !!user,
};
};
// components/GoogleSignInButton.tsx
import React from 'react';
import { TouchableOpacity, Text, View, ActivityIndicator } from 'react-native';
import { useGoogleAuth } from '../hooks/useGoogleAuth';
export const GoogleSignInButton: React.FC = () => {
const { user, loading, error, signIn, signOut, isSignedIn } = useGoogleAuth();
if (loading) {
return (
<View className="flex-row items-center justify-center bg-white border border-gray-300 rounded-lg px-6 py-3">
<ActivityIndicator size="small" color="#4285F4" />
<Text className="ml-2 text-gray-600">처리 중...</Text>
</View>
);
}
if (isSignedIn && user) {
return (
<View className="bg-white border border-gray-300 rounded-lg p-4">
<Text className="text-gray-800 font-semibold">안녕하세요, {user.name}님!</Text>
<Text className="text-gray-600 text-sm">{user.email}</Text>
{error && <Text className="text-red-500 text-sm mt-2">{error}</Text>}
<TouchableOpacity
onPress={signOut}
className="mt-3 bg-red-500 rounded-lg px-4 py-2"
>
<Text className="text-white text-center font-semibold">로그아웃</Text>
</TouchableOpacity>
</View>
);
}
return (
<TouchableOpacity
onPress={signIn}
className="flex-row items-center justify-center bg-white border border-gray-300 rounded-lg px-6 py-3 shadow-sm"
disabled={loading}
>
{/* Google 아이콘 */}
<View className="w-5 h-5 mr-3">
<Text>🔵</Text> {/* 실제로는 Google 아이콘 이미지 사용 */}
</View>
<Text className="text-gray-700 font-semibold">Google로 로그인</Text>
{error && (
<View className="mt-2">
<Text className="text-red-500 text-sm">{error}</Text>
</View>
)}
</TouchableOpacity>
);
};
// app/(auth)/login.tsx - 로그인 화면 예제
import React from 'react';
import { View, Text, SafeAreaView } from 'react-native';
import { GoogleSignInButton } from '../../components/GoogleSignInButton';
export default function LoginScreen() {
return (
<SafeAreaView className="flex-1 bg-white">
<View className="flex-1 justify-center items-center px-6">
{/* 앱 로고 */}
<View className="mb-12">
<Text className="text-4xl font-bold text-center mb-2" style={{ color: '#58CC02' }}>
🌱 쑥쑥약속
</Text>
<Text className="text-gray-600 text-center">
부모와 아이가 함께 성장하는{'\n'}약속 관리 앱
</Text>
</View>
{/* Google 로그인 버튼 */}
<View className="w-full">
<GoogleSignInButton />
</View>
{/* 추가 정보 */}
<View className="mt-8">
<Text className="text-gray-500 text-sm text-center">
로그인 후 부모와 아이 계정을{'\n'}연결할 수 있습니다
</Text>
</View>
</View>
</SafeAreaView>
);
}

필요한 패키치 설치

# Google Sign-In 패키지 설치
npm install @react-native-google-signin/google-signin
# Development client 설치 (이미 설치했다면 생략)
npx expo install expo-dev-client

EAS Build Profile 설정

이건 쉽게 생각하면, expo로 시작했으니, react native cli 초기 설정을 한다고 생각하면 됨

// 예시
{
"cli": {
"version": ">= 0.52.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": {
"resourceClass": "m1-medium"
}
},
"preview": {
"distribution": "internal"
},
"production": {}
}
}

이렇게 설정하면 firebase 없이 구글 로그인 성공 !