💡 firebase에 관련된 로직들은 firebase.js에서 만들고 필요한 컴포넌트에서 import하여 사용한다.
//1.Google 공급자 인스턴스를 만들기
import { GoogleAuthProvider } from "firebase/auth";
const provider = new GoogleAuthProvider();
//2.Google 공급자 개체를 사용하여 Firebase에 인증.
import { getAuth, signInWithPopup, GoogleAuthProvider } from "firebase/auth";
const auth = getAuth();
signInWithPopup(auth, provider) //signInWithPopup을 사용하면 팝업창으로 인증
.then((result) => {
const credential = GoogleAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
const user = result.user;
}).catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
const email = error.customData.email;
const credential = GoogleAuthProvider.credentialFromError(error);
});
// api/firebase
import { initializeApp } from "firebase/app";
import { getAuth,signInWithPopup,GoogleAuthProvider } from "firebase/auth";
const auth = getAuth();
export async function login() {
return signInWithPopup(auth, provider)
.then((result) => {
const user = result.user; // 사용자의 정보가 user안에 들어온다.
console.log(user);
return user; // 사용자의 정보가 있다면 return
})
.catch(console.error); //catch(error => console.error(error))
//콜백함수의 전달인자와 호출하는 인자가 동일할경우 생략가능
}
// components/Navbar.js
<Button text="Login" onClick={login}></Button>
// firebase 로그아웃
import { getAuth, signOut } from "firebase/auth";
const auth = getAuth();
signOut(auth).then(() => {
// Sign-out successful.
}).catch((error) => {
// An error happened.
});
// api/firebase logout추가
import {
getAuth,
signInWithPopup,
GoogleAuthProvider,
signOut
} from "firebase/auth";
export async function logout() {
return signOut(auth).then(()=>null);
}
// component/Navbar.js
import { Link } from "react-router-dom";
import { FiShoppingBag, FiMenu } from "react-icons/fi";
import Button from "./ui/Button";
import { login, logout } from "../api/firebase;
import { useState } from "react";
function Navbar() {
const [user, setUser]=useState();
const handleLogin=()=>{
login().then(setUser); //then( user => setUser(user))
};
const handleLogout=()=>{
logout().then(setUser);
};
return (
<header>
<Link to="/">
<h1>MIOGY STUDIO</h1>
</Link>
<nav>
<Link to="/products">Products</Link>
{!user && <Button text="Login" onClick={handleLogin}></Button>}
{user && <Button text="Logout" onClick={handleLogout}></Button>}
</nav>
</header>
);
}
export default Navbar;
🚧 로그인/로그아웃이 완성되었다.
여기서 로그인 상태에 새로고침을 하면 어떻게 될까? 그렇다. 새로고침을 하면 로그인한 상태를 보관하는 컴포넌트가 재렌더링되면서 상태도 다시 되돌아간다.
firebase에서는 사용자가 로그인하게 되면 로그인한 사용자의 정보 즉 세션을 보관하고 있다.
그래서 사용자가 새로고침을 해도 로그인한 사용자의 정보가 남아있다면 사용할수 있지만 현재 임의로 컴포넌트내에 user라는 state에 의존해서 로그인 만들었기 때문에 재렌더링하면 다시 state가 null로 바뀌는것을 볼수있다.
어떻게 해결할 수 있을까?
로그인한 사용자에 대한 정보가 필요한 각 앱 페이지에 대해 전역 인증 개체에 관찰자를 연결합니다. 이 관찰자는 사용자의 로그인 상태가 변경될 때마다 호출됩니다.
onAuthStateChanged 메서드를 사용하여 관찰자를 연결합니다. 사용자가 성공적으로 로그인하면 관찰자에서 사용자에 대한 정보를 얻을 수 있습니다.
📌 ref : onAuthStateChanged 사용하기
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
const uid = user.uid;
} else {
// User is signed out
// ...
}
});
//api/firebase
import {
getAuth,
signInWithPopup,
GoogleAuthProvider,
signOut,
onAuthStateChanged,
} from "firebase/auth";
//사용자의 상태를 확인추가
export function onUserStateChange(callback) {
onAuthStateChanged(auth, (user) => {
callback(user);
});
}
// components/Navbar.js
import { Link } from "react-router-dom";
import { FiShoppingBag, FiMenu } from "react-icons/fi";
import User from "./User";
import Button from "./ui/Button";
import { login, logout, onUserStateChange } from "../api/firebase;
import { useState } from "react";
function Navbar() {
const [user,setUser]=userState();
useEffect(()=>{
onUserStateChange((user)=>{
setUser(user); // user가 변경될때마다 컴포넌트에 state를 변경
})
}, [])
return (
const handleLogin=()=>{
login().then(setUser); //then( user => setUser(user))
};
const handleLogout=()=>{
logout().then(setUser);
};
return (
<header>
<Link to="/">
<h1>MIOGY STUDIO</h1>
</Link>
<nav>
<Link to="/products">Products</Link>
{!user && <Button text="Login" onClick={handleLogin}></Button>}
{user && <Button text="Logout" onClick={handleLogout}></Button>}
</nav>
</header>
);
}
export default Navbar;
firebase로 만들어보는 로그인 간단한듯하지만 어렵다.
로그인기능을 위해 getAuth와 signInWithPopup가 필요하고 의존성으로 인해 firebase관련 로직들은 firebase내에서 관리한다.
로그아웃은 getAuth와 signOut이 필요하며 signOut에 auth를 담는다. (에러조심)
Navbar에서 state로 user의 상태를 관리하게 되면 새로고침시 재렌더링 되므로 firebase에서 제공하는 onAuthStateChanged()를 사용하여 사용자로그인 상태를 관리한다.
필요한것은 getAuth와 onAuthStateChanged이며 user를 callback에 담아 onUserStateChange라는 함수를 만들어 Navbar 컴포넌트에 useEffect에 담는다.
useEffect에 담긴 onUserStateChange함수는 처음 마운트될때 호출되고 콜백함수가 실행되면 user를 담아 상태에 따라 state가 변경된다.
여기까지 오늘의 정리 끝.