์์
์ด ๋๋๊ธฐ์ ์ ํ๋ฉด์ด๋์ด๋ ์ข
๋ฃ๋ฅผ ํ๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์์ผ๋ฏ๋ก
ํ์ฌ ์งํ์ค์ธ ์์
์ ์ทจ์ํ๋ ํด๋ฆฐ์
ํจ์๋ฅผ ์ถ๊ฐํ ๊ฒ
https://blog.naver.com/drv983/223181897289
useLogout.js์ ์ทจ์ ์ฌ๋ถ๋ฅผ ๋ด๋ state๋ฅผ ์์ฑํ๋ค.
const [isCancelled, setIsCancelled] = useState(false);
useEffect(() => {
setIsCancelled(false);
// ๋ก๊ทธ์์ ์์
์ค ์ค๊ฐ์ ์ฌ๋ผ์ง๋ค๋ฉด
// useEffect์ return์ด unmount๋ ๋์ ์์
(ํด๋ฆฐ์
)์ด ๋๋ค.
return () => setIsCancelled(true);
}, []);
return { logout, error, isPending };
};
๊ทธ๋ฆฌ๊ณ ์์ try catch๋ ๋ฐ๊ฟ์ค๋ค.
const logout = async () => {
setError(null);
setIsPending(true);
try {
//ํ์ด์ด๋ฒ ์ด์ค ๋ก๊ทธ์์
await fireauth.signOut();
//๋ก๊ทธ์์ ์ก์
๋์คํจ์น
dispatch({ type: "LOGOUT" });
//์์
์ด ์ทจ์๋์๋ค๋ฉด state์
๋ฐ์ดํธ๋ฅผ ํ์ง ์๋๋ค.
if (!isCancelled) {
setIsPending(false);
setError(null);
}
} catch (err) {
if (!isCancelled) {
console.log(err.message);
setError(err.message);
setIsPending(false);
}
}
};
useSignup.js๋ ๋์ผํ๊ฒ ์ถ๊ฐํ๋ค.
const [isCancelled, setIsCancelled] = useState(false);
const signup = async (email, password, displayName) => {
setError(null);
setIsPending(true);
try {
// ์ด๋ฉ์ผ,ํจ์ค์๋๋ก ๊ฐ์
const res = await fireauth.createUserWithEmailAndPassword(
email,
password
);
if (!res) {
throw new Error("๊ฐ์
์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.");
}
// ์ ์ ํ๋กํ์ผ์ ์ด๋ฆ์ ์
๋ฐ์ดํธ
await res.user.updateProfile({ displayName: displayName });
// ์ ์ ์ ๋ณด๋ฅผ state์ ์ ์ฅํ๋ค.
dispatch({ type: "LOGIN", payload: res.user });
//์์
์ด ์ทจ์๋์๋ค๋ฉด ์คํ
์ดํธ ์
๋ฐ์ดํธ๋ฅผ ํ์ง ์๋๋ค.
if (!isCancelled) {
setError(null);
setIsPending(false);
}
} catch (err) {
if (!isCancelled) {
console.log(err.message);
setError(err.message);
setIsPending(false);
}
}
};
useEffect(() => {
setIsCancelled(false);
// ๋ก๊ทธ์์ ์์
์ค ์ค๊ฐ์ ์ฌ๋ผ์ง๋ค๋ฉด
// useEffect์ return์ด unmount๋ ๋์ ์์
(ํด๋ฆฐ์
)์ด ๋๋ค.
return () => setIsCancelled(true);
}, []);
return { signup, error, isPending };
};
์ค๋ฅ๊ฐ ๋๋์ง ํ
์คํธํด๋ด์ผํจ
useLogin.js์์ฑ
logout.js ๋ณต์ฌํด์ ์ฌ์ฉํจ
import { useEffect, useState } from "react";
import { useAuthContext } from "./useAuthContext";
import { fireauth } from "../firebase/config";
export const useLogin = () => {
const [error, setError] = useState();
const [isPending, setIsPending] = useState(false);
const { dispatch } = useAuthContext();
const [isCancelled, setIsCancelled] = useState(false);
// ๋ก๊ทธ์ธ ์์
์ค ์ค๊ฐ์ ์ฌ๋ผ์ง๋ค๋ฉด
// useEffect์ return์ด unmount๋ ๋์ ์์
(ํด๋ฆฐ์
)์ด ๋๋ค.
const login = async (email, password) => {
setError(null);
setIsPending(true);
try {
//ํ์ด์ด๋ฒ ์ด์ค ๋ก๊ทธ์ธ
const res = await fireauth.signInWithEmailAndPassword(email, password);
//๋ก๊ทธ์ธ ์ก์
๋์คํจ์น
dispatch({ type: "LOGIN", payload: res.user });
//์์
์ด ์ทจ์๋์๋ค๋ฉด state์
๋ฐ์ดํธ๋ฅผ ํ์ง ์๋๋ค.
if (!isCancelled) {
setIsPending(false);
setError(null);
}
} catch (err) {
if (!isCancelled) {
console.log(err.message);
setError(err.message);
setIsPending(false);
}
}
};
useEffect(() => {
setIsCancelled(false);
// ๋ก๊ทธ์ธ ์์
์ค ์ค๊ฐ์ ์ฌ๋ผ์ง๋ค๋ฉด
// useEffect์ return์ด unmount๋ ๋์ ์์
(ํด๋ฆฐ์
)์ด ๋๋ค.
return () => setIsCancelled(true);
}, []);
return { login, error, isPending };
};
Login.jsx์์ useLogin hook์ ์ฌ์ฉํ๋ค.
const Login = () => {
const [email, setEmail] = useState(""); //์ด๋ฉ์ผ
const [password, setPassword] = useState(""); //๋น๋ฐ๋ฒํธ
const { login, error, isPending } = useLogin(); //useLoginํ
๊ฐ์ ธ์ค๊ธฐ
๋ง๋ค์ด ๋์ useLogin hook์ ๊ฐ์ ธ์จ๋ค.
๊ทธ๋ฆฌ๊ณ handleSubmit ์ ์ ์ฅํ email๊ณผ password๋ฅผ ์ฌ์ฉํด login ํจ์๋ฅผ ์คํํ๋ค.
const handleSubmit = (e) => {
e.preventDefault();
login(email, password);
};
{!isPending && <button className="btn">๋ก๊ทธ์ธ</button>}
{isPending && (
<button className="btn " disabled>
๋ก๋ฉ์ค...
</button>
)}
{error && <p>{error}</p>}
๋ก๋ฉ์ค์ด ์๋ ๋๋ง ๋ก๊ทธ์ธ ์ถ๋ ฅํ๊ณ ๋ก๋ฉ ์ค์ผ ๊ฒฝ์ฐ ๋ฒํผ์ ํด๋ฆญํ์ง ๋ชปํ๋๋ก ํ๋ค.
๊ทธ๋ฆฌ๊ณ ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด ์๋ฌ๋ฅผ ์ถ๋ ฅํ๋ค.
ํ
์คํธํด๋ด
์๊น ๊ฐ์
ํด๋จ๋ hong์จ์ ์ด๋ฉ์ผ๋ก ๋ก๊ทธ์ธ ์๋๋ฅผ ํ๋ค.
์ฌ๋ฐ๋ฅด๊ฒ ์
๋ ฅ ์
๋ก๊ทธ์ธ ํ๊ณ ๋์ ๋ก๊ทธ์์ ๋ฒํผ ํด๋ฆญ ์
์ด๋ฉ์ผ ๋น๋ฐ๋ฒํธ ํ๋ฆฌ๊ฒ ์
๋ ฅํ์ ๊ฒฝ์ฐ
๋ก๋ฉ์ค์ผ ๋ ์ทจ์ํ๋ฉด ๋ก๊ทธ์ธ ์ทจ์๊ฐ ๋๋์ง ํ
์คํธ ํด๋ณผ๋ ค๊ณ ํ๋๋ฐ
๋๋ฌด ๋นจ๋ผ์ ์ทจ์ ํ์ธ์ด ์ด๋ ค์ ใ
ํ ใ
ใ
๊ทธ๋ฆฌ๊ณ Navbar์ ์ธ์ฆ ๋ ์ ์ ์ผ ๊ฒฝ์ฐ ๋ก๊ทธ์์๋ง ๋ณด์ด๊ฒ ํด๋ณด์
Navbar.jsx
//CSS ๋ชจ๋์ ์ฌ์ฉํ๋ฉด CSSํด๋์ค๊ฐ ๋ค๋ฅธ ์ปดํฌ๋ํธ์๋ ์ ์ฉ๋๋๊ฒ์ ๋ฐฉ์งํจ
import { useAuthContext } from "../hooks/useAuthContext";
import { useLogout } from "../hooks/useLogout";
import styles from "./Navbar.module.css";
import { Link } from "react-router-dom";
const Navbar = () => {
//์ ์ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
const { user } = useAuthContext();
//๋ก๊ทธ์์ ๋ฉ์๋
const { logout } = useLogout();
return (
<nav className={styles.navbar}>
<ul>
<li className={styles.title}>
<Link to="/">myMoney</Link>
</li>
{!user && (
<>
<li>
<Link to="/login">๋ก๊ทธ์ธ</Link>
</li>
<li>
<Link to="/signup">๊ฐ์
</Link>
</li>
</>
)}
{user && (
<>
<li>์๋
ํ์ธ์, {user.displayName}</li>
<li>
<button className="btn" onClick={logout}>
๋ก๊ทธ์์
</button>
</li>
</>
)}
</ul>
</nav>
);
};
export default Navbar;
displayName์ ์๋์ ๊ฐ์ด ์ ์ ์๋ค.
ํ
์คํธํ๋ค.
๋ก๊ทธ์ธ ์ํ
๋ก๊ทธ์์ ์ํ
ํ์ด์ด ๋ฒ ์ด์ค๋ ๋ก๊ทธ์ธ ์ ์ผ์ ํ ๊ธฐ๊ฐ๋์์
๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์์ (์ธ์
์ฒ๋ผ)
๊ทธ๋์
ํ์ฌ๋ ์๋ก๊ณ ์นจ์ ํ ๊ฒฝ์ฐ์๋ ๋ก๊ทธ์์์ด ๋๋ฒ๋ฆฌ๊ธฐ ๋๋ฌธ์
ํ์ฌ ๋ก๊ทธ์ธ ๋์ด์์ผ๋ฉด ๊ฐ์ ธ์ค๋๊ฑธ๋ก ์์ ํด ๋ณด๊ฒ ์.
AuthContext.jsx
// ์ธ์ฆ ํ๋ก๋ฐ์ด๋์์ useReducer๋ฅผ ์ฌ์ฉํ๊ณ ์คํ
์ดํธ์ ๋์คํจ์น๋ฅผ ์ ๊ณต
export const AuthContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(authReducer, {
user: null,
authIsReady: false,
});
// console.log("AuthContext state: ", state);
authIsReady ์ถ๊ฐํ๊ณ ์๋์ useState์ถ๊ฐ
// ์ธ์ฆ ํ๋ก๋ฐ์ด๋์์ useReducer๋ฅผ ์ฌ์ฉํ๊ณ ์คํ
์ดํธ์ ๋์คํจ์น๋ฅผ ์ ๊ณต
export const AuthContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(authReducer, {
user: null,
authIsReady: false,
});
// console.log("AuthContext state: ", state);
useEffect(() => {
// ์ฒ์ ์์์ ์ ์ ๋ฅผ ํ์ธํด์ ์ ์ ์ ๋ณด(์์ผ๋ฉดNULL)์ ์ธ์ฆํ์ธ ์ก์
๋์คํจ์น
const unsub = fireauth.onAuthStateChanged((user) => {
dispatch({ type: "AUTH_IS_READY", payload: user });
unsub();
});
}, []);
๊ทธ๋ฆฌ๊ณ ์๋จ ๋ฆฌ๋์ ๋ฉ์๋์ dispatch type์ถ๊ฐํ๋ค.
//๋ฆฌ๋์ ๋ฉ์๋
export const authReducer = (state, action) => {
switch (action.type) {
case "LOGIN":
return { ...state, user: action.payload };
case "LOGOUT":
return { ...state, user: null };
case "AUTH_IS_READY":
return { ...state, user: action.payload, authIsReady: true };
default:
return state;
}
};
โ
๋ก๊ทธ์ธ ํ์ด์ง๋ฅผ ๋ค์ ์๋ก๊ณ ์นจํด๋ ํ์ด์ด๋ฒ ์ด์ค์ ์ธ์ฆ์ด ๋ ์ํ๋ผ๋ฉด
๊ณ์ํด์ ๋ก๊ทธ์ธ ์ํ๋ฅผ ์ ์งํ๋ค.
์๋ก๊ณ ์นจ์ ํด๋ ๊ทธ๋๋ก ์ ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์จ๋ค.
App.jsx
import { BrowserRouter, Route, Routes } from "react-router-dom";
import "./App.css";
import Home from "./pages/home/Home";
import Login from "./pages/login/Login";
import Signup from "./pages/signup/Signup";
import Navbar from "./Components/Navbar";
import { useAuthContext } from "./hooks/useAuthContext";
function App() {
//ํ์ด์ด ๋ฒ ์ด์ค ์ธ์ฆํ์ธ์ ํ ํ ์ฑ์ ์ฌ์ฉ๊ฐ๋ฅ
const { authIsReady } = useAuthContext();
return (
<>
<div className="App">
{authIsReady && (
<BrowserRouter>
<Navbar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
</Routes>
</BrowserRouter>
)}
</div>
</>
);
}
export default App;