Firebase를 사용하여 사용자의 로그인 상태를 관리하는 시스템을 구현하였다. Zustand의 persist
미들웨어를 사용하여 사용자 상태를 Local Storage에 저장함으로써 페이지를 새로고침하거나 재방문해도 로그인 상태가 유지되게 했다.
그런데 테스트 중 사용자의 로그인 상태가 예상대로 동기화되지 않는 문제가 발생하였다.
AuthStateObserver
컴포넌트는 Firebase의 인증 상태 변화를 감지하고, 사용자가 로그인할 때마다 Firebase Firestore에서 사용자 정보를 가져와 Zustand 상태에 업데이트한다. 이 과정은 비동기적으로 처리되며, 인증 토큰 갱신에 실패하거나 사용자 정보가 없는 경우 상태를 null로 설정하여 로그아웃 처리한다.// @store/useUserStore.ts
import create from 'zustand';
import { persist } from 'zustand/middleware';
import { UserType } from '@/types/User';
type State = {
user: UserType | null;
};
interface Action {
setUser: (user: UserType | null) => void;
}
export const useUserStore = create<State & Action>()(
persist(
(set) => ({
user: null,
setUser: (user: UserType | null) => set(() => ({ user })),
}),
{
name: 'user-store',
getStorage: () => localStorage,
},
),
);
// @components/auth/AuthStateObserver.tsx
import { useEffect } from 'react';
import { doc, getDoc } from 'firebase/firestore';
import { auth, db } from '@services/firebaseConfig';
import { useUserStore } from '@store/useUserStore';
import { UserType } from '@/types/User';
function AuthStateObserver() {
const { setUser } = useUserStore();
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
if (user) {
// 사용자가 로그인한 상태
user
.getIdToken()
.then(async () => {
const userRef = doc(db, 'users', user.uid);
const userDoc = await getDoc(userRef);
setUser(userDoc.data() as UserType);
})
.catch((error) => {
console.error('Token renewal error:', error);
setUser(null);
});
} else {
setUser(null);
}
});
return () => unsubscribe();
}, [setUser]);
return null;
}
export default AuthStateObserver;