- 사용자 정보가 바뀌는 상황을 크게 3가지로 나눌 수 있다. 로그인할 때, 로그아웃할 때, 창이 꺼졌을 때.
- 먼저 로그인은 firebase auth에서 제공하는 GoogleAuthProvider 와 signInWithRedirect API 로 구현하였다.
export const auth = firebase.auth();
const signIn = useCallback(async () => {
await auth.setPersistence(firebase.auth.Auth.Persistence.SESSION);
const provider = new firebase.auth.GoogleAuthProvider();
dispatch(actions.setLoading(true));
try {
await auth.signInWithRedirect(provider);
} catch {
toast('인터넷이 불안정합니다. 다시 시도해주세요.');
}
}, [dispatch]);
(Persistence.SESSION 에 대한 내용은 뒤로 미룬다.)
signInWithRedirect 에서 정상적으로 구글 로그인에 성공하면, 'auth' instance 의 상태가 변화한다.
- auth 상태가 변할 때 onAuthStateChanged observer를 이용해 유저 정보가 있다면(로그인할 때) redux에 유저 정보를 저장한다. 유저 정보가 없다면(로그아웃할 때) redux의 유저 정보를 null로 명시해서 버그를 사전에 방지한다.
useEffect(() => {
auth.onAuthStateChanged((currentUser) => {
if (currentUser) {
dispatch(actions.setValue('email', currentUser.email));
dispatch(actions.setValue('uid', currentUser.uid));
} else {
dispatch(actions.setValue('email', null));
dispatch(actions.setValue('uid', null));
}
dispatch(actions.setLoading(false));
});
}, [dispatch]);
- 로그아웃도 firebase auth의 signOut 함수를 사용하여 간단하게 구현한다.
const signOut = useCallback(async () => {
dispatch(actions.setLoading(true));
dispatch(actions.reset());
await auth.signOut();
}, [dispatch]);
이때, reset action을 통해 redux의 유저 정보도 명시적으로 제거한다.
- 완성된 useAuth hook 코드이다.
import { useCallback, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { auth } from '@U/initializer/firebase';
import { actions } from '@/redux/user/state';
...
import firebase from 'firebase/app';
...
const useAuth = () => {
const dispatch = useDispatch();
useEffect(() => {
auth.onAuthStateChanged((currentUser) => {
if (currentUser) {
dispatch(actions.setValue('email', currentUser.email));
dispatch(actions.setValue('uid', currentUser.uid));
} else {
dispatch(actions.setValue('email', null));
dispatch(actions.setValue('uid', null));
}
dispatch(actions.setLoading(false));
});
}, [dispatch]);
const signIn = useCallback(async () => {
await auth.setPersistence(firebase.auth.Auth.Persistence.SESSION);
const provider = new firebase.auth.GoogleAuthProvider();
dispatch(actions.setLoading(true));
try {
await auth.signInWithRedirect(provider);
} catch {
toast('인터넷이 불안정합니다. 다시 시도해주세요.');
}
}, [dispatch]);
const signOut = useCallback(async () => {
dispatch(actions.setLoading(true));
dispatch(actions.reset());
try {
await auth.signOut();
} finally {
...
}
}, [dispatch]);
return { signIn, signOut };
};
export default useAuth;
- 사용자가 로그인/로그아웃 버튼을 누르지 않더라도, 브라우저 창이 꺼지면 로그인이 풀리도록 구현하고자 했다.
먼저 2번 과정에서 Persistence.SESSION을 통해 firebase auth instance의 지속성을 session 으로 설정했다. (https://firebase.google.com/docs/auth/web/auth-state-persistence#supported_types_of_auth_state_persistence)
await auth.setPersistence(firebase.auth.Auth.Persistence.SESSION);
redux도 마찬가지로 session storage를 사용하도록 redux-persist 옵션값을 설정했다.
import { persistStore, persistReducer } from 'redux-persist';
import sessionStorage from 'redux-persist/lib/storage/session';
const rootPersistConfig = {
key: '',
storage: sessionStorage,
whitelist: [],
};
const persistedReducer = persistReducer(rootPersistConfig, reducer);
이제 브라우저 창을 닫으면 firebase auth instance와 redux storage가 모두 비워진다!