React Native

React Native - 구글 로그인 (3) 로그인 상태 확인 onAuthStateChanged

크런키스틱 2025. 2. 1. 22:20
728x90

어플에서 로그인 기능을 구현했다면 추가적으로 필요한 기능은 로그인 상태를 확인하는 기능이 필요하다. 일반적으로 AsyncStorage에 인증 정보를 저장하는 방법을 사용하는데,  Firebase로 인증을 구현했다면 Firebase에서 인증은 자동으로 인증상태를 유지해주기 때문에 onAuthStateChanged를 통해 굉장히 쉽게 구현할 수 있다.

 

onAuthStateChanged는 event listener와 같이 event를 감지하기 위해 계속 대기하고 있고, 유저 상태에 변화가 생기면 그 변화를 감지하고 해당 로직을 수행한다.

 

1. 코드

// App.tsx

import './global.css';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import Navigation from 'navigation';
import {useNetInfo} from '@react-native-community/netinfo';
import NoNetwork from '@/screen/noNetwork';
import auth, {FirebaseAuthTypes} from '@react-native-firebase/auth';
import {useEffect, useState} from 'react';

interface User {
  uid: string;
  email: string;
  displayName: string;
}

const App = () => {
  const [user, setUser] = useState<User | null>(null);

  // Check Network
  const {isConnected} = useNetInfo();

  // Check Auth
  const onSubscribeAuth = (user: FirebaseAuthTypes.User | null) => {
    if (user) {
      setUser({
        uid: user.uid,
        email: user.email!,
        displayName: user.displayName!,
      });
    } else {
      setUser(null);
    }
  };

  useEffect(() => {
    const onUnsubscribeAuth = auth().onAuthStateChanged(onSubscribeAuth);
    return onUnsubscribeAuth;
  }, []);

  return (
    <SafeAreaProvider>
      {isConnected ? <Navigation user={user} /> : <NoNetwork />}
    </SafeAreaProvider>
  );
};

export default App;
// navigation.tsx

...
const Navigation = ({
  user,
}: {
  user: {
    uid: string;
    email: string;
    displayName: string;
  } | null;
}) => {
  return (
    <NavigationContainer>
      {user ? (
        <Stack.Navigator
          initialRouteName="SignIn"
          screenOptions={{headerShown: false, animation: 'flip'}}>
          <Stack.Screen name="SignIn" component={SignInScreen} />
        </Stack.Navigator>
      ) : (
        <Stack.Navigator
          initialRouteName="Main"
          screenOptions={{headerShown: false, animation: 'flip'}}>
          <Stack.Screen name="Main" component={TabStack} />
          <Stack.Screen
            name="Test"
            component={TestScreen}
            options={({navigation}) => ({
              headerShown: true,
              headerBackVisible: false,
              headerLeft: () => (
                <AngleLeft onPress={() => navigation.goBack()} />
              ),
            })}
          />
        </Stack.Navigator>
      )}
    </NavigationContainer>
  );
};
 ...

 

위에 예시 코드는 굉장히 간단하게 처리한 로직이다.

 

onAuthStateChanged는 위에 언급했듯이 사용자의 인증 상태가 바뀔 때마다 호출된다. 하지만 어플 구동 시에만 사용자의 로그인 상태를 확인하고 그 이후에는 인증 정보가 바뀌어도(ex. 로그아웃) navigation과 부수적인 기능 구현을 통해 처리하면 되기 때문에 더 이상 호출될 필요가 없다. 따라서, onUnsubscribeAuth 함수를 선언하고 호출하여 사용자 인증상태가 바뀔 때마다 onAuthStateChanged가 실행되는 일을 방지한다.

 

onAuthStateChanged 내의 로직은 본인이 구상하는데로 구현하면 되는데, 위의 예시는 user state를 선언하고 그 값을 navigation에 넘겨 user 값의 유무에 따라 navigation의 컴포넌트를 달리 로드하는 방식이다.

 

다만, 위 코드는 편의를 위해 간단하게 구성한 것으로 본인은 user가 아닌 isLoggedIn과 같이 boolean 타입의 state를 통해 navigation의 컴포넌트를 구분하고, 실제 user 값은 전역상태관리(context, recoil 등)을 통해 관리하는 것이 더 깔끔하다고 생각한다.

 

2. 실행

실행해보면 아래와 같이 어플을 재시작하고 새로고침할 때마다 로그인 여부에 따라서 화면이 다르게 표현되는 것을 확인할 수 있다.

다만, 시작할 때 인증 상태를 체크하기 위해서 화면이 다소 늦게 로딩되는 것을 확인할 수 있는데 이는 추후에 스플래쉬 화면 처리 혹은 root 화면을 만들어 그 안에서 처리하거나 로딩 화면을 만들어 제공하면 사용자에게 자연스러운 화면 전환을 제공할 수 있다.

728x90