์ ์ผ ๋จผ์ ๊ตฌํํ ๊ธฐ๋ฅ์ ๋ก๊ทธ์ธ!
๊ฐ๋จํ๊ฒ ๋ก๊ทธ์ธ์ด๋ผ๊ณ ๋ง ๋งํ์ง๋ง ๋ผ์ฐํ
์ฒ๋ฆฌ๋ถํฐ ํ์๊ฐ์
, ๋ก๊ทธ์ธ, ๋ก๊ทธ์์ ๋ฑ ์ฌ์ฉ์ ์ธ์ฆ๊ณผ ๊ด๋ จ๋ ๊ธฐ๋ฅ์ ๋ชจ๋ ๊ตฌํํ๋ค
์ฌ์ฉ์ ์ธ์ฆ ๋ฐฉ๋ฒ์ผ๋ก firebase api๋ jwt
๋ฅผ ์ง์ํ๋ค. ์ฌ์ค ์ธ์
์ฟ ํค ๋ฐฉ์์ ํ๋ฒ ์ฌ์ฉํด ๋ณด๊ณ ์ถ์๋๋ฐ ์์ฝ๊ฒ๋ ๋ฐฑ์๋๋ฅผ ๊ตฌํํ ์ค ๋ชจ๋ฅด๊ณ firebase api๋ฅผ ์ฌ์ฉํ๋ ์
์ฅ์ด๋ผ ์ด๋ฒ ํ๋ก์ ํธ์์๋ ์ด๋ ต๋ค๊ณ ํ๋จํ๋ค.
์ธ์ ์ฟ ํค๋ก ๋ง๋ค๊ณ ์ถ์ ์ด์ ๋ jwt๋ฅผ ๋จ์ฉํ๊ณ ์๋ค๋ ์๊ฐ์ด ๋ค์๊ธฐ ๋๋ฌธ์ด๋ค. ๋ firebase auth๋ง ์ฌ์ฉํ๋ค๋ณด๋ ์์ฐ์ค๋ฝ๊ฒ jwt ๋ง ์ฌ์ฉํ๊ฒ ๋์๋๋ฐ, ์ธ์ ์ฟ ํค๋ ํ๋ฅญํ ์ ์ ์ธ์ฆ ๋ฐฉ์์ด๋ผ๊ณ ๋ค์๊ณ ๋ฌด์๋ณด๋ค ์ jwt๋ฅผ ์ฌ์ฉํ๋์ง๋ ๋ชจ๋ฅด๋ ์ฑ ๊ณ์ ์ฌ์ฉํ๋๊ฒ ์ฐ์ฐํ ๊ธฐ๋ถ์ด ๋ค์๋ค. ์ง๊ธ์ ์ด์ฉ์์์ด jwt๋ฅผ ์ฌ์ฉํ์ง๋ง ํ์ node.js๋ฅผ ๊ณต๋ถํ๊ณ ๋ฐฑ์๋์ชฝ์ ๋ง์ง๊ฒ ๋๋ค๋ฉด ๊ผญ ์ธ์ ์ฟ ํค ๋ฐฉ์์ผ๋ก ๋ฆฌํฉํ ๋ง ํด ๋ณผ ๊ฒ์ด๋ค.
๐ฆsrc
โฃ ๐api
โ โ ๐firebase.jsx // firebase api ๋ชจ์
โฃ ๐components
โ โ ๐SignForm.jsx // ๋ก๊ทธ์ธ,ํ์๊ฐ์
form
โฃ ๐pages
โ โฃ ๐Login.jsx // ๋ก๊ทธ์ธ
โ โ ๐SignUp.jsx //ํ์๊ฐ์
โฃ ๐store
โ โ ๐AuthContext.jsx //์ ์ ์ธ์ฆ ์ ๋ณด
path: "/sign_in", element: <Login />
path: "/sign_up", element: <SignUp />
Login, SignUp ์ปดํฌ๋ํธ๋ก๋ถํฐ isLogin props๋ฅผ ๋ฐ์ true์ด๋ฉด ๋ก๊ทธ์ธ ๊ธฐ๋ฅ, false์ด๋ฉด ๊ฐ์ ๊ธฐ๋ฅ์ ์คํํ๋ค.
import React, { useRef, useState } from "react";
import { useNavigate } from "react-router";
import { createUser, loginUser } from "../api/firebase";
import Button from "./ui/Button";
export default function SignForm({ isLogin }) {
const [user, setUser] = useState({});
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState();
const emailRef = useRef();
const passwordRef = useRef();
const btnText = isLogin ? "๋ก๊ทธ์ธ" : "๊ฐ์
ํ๊ธฐ";
const navigate = useNavigate();
const onChangeHandler = (e) => {
const { name, value } = e.target;
setUser((state) => ({ ...state, [name]: value }));
};
const onSubmitHandler = async (e) => {
e.preventDefault();
setIsLoading(true);
const response = await (isLogin
? loginUser(user, setError)
: createUser(user, setError));
response && (isLogin ? navigate("/") : navigate("/login"));
setIsLoading(false);
setUser({});
emailRef.current.focus();
};
return (
<form onSubmit={onSubmitHandler}>
<input
ref={emailRef}
type="email"
name="email"
value={user.email || ""}
onChange={onChangeHandler}
autoComplete="off"
placeholder="์์ด๋"
required
/>
<input
ref={passwordRef}
type="password"
name="password"
value={user.password || ""}
onChange={onChangeHandler}
autoComplete="off"
placeholder="๋น๋ฐ๋ฒํธ"
minLength="6"
required
/>
{!isLoading && <Button text={btnText} />}
{isLoading && <p>Sending request...</p>}
{error && <p>{error}</p>}
</form>
);
}
ํ์๊ฐ์
, ๋ก๊ทธ์ธ์ ์ ์์ ์ผ๋ก ์ํ์ด ๋๋๋ฉด ๊ฒฐ๊ณผ๊ฐ์ ๋ฐํํ๋ค.
์ธ์ฆ ์ํ๋ ์ธ์
์ด ์ข
๋ฃ๋๋ฉด ์ญ์ ๋๋ค
...
// ํ์๊ฐ์
export const createUser = async ({ email, password }, callback) => {
return createUserWithEmailAndPassword(auth, email, password)
.then((res) => {
alert("๊ฐ์
์๋ฃ");
return res;
})
.catch((error) => callback(`๊ฐ์
์ ์คํจํ์ต๋๋ค. ${error.code}`));
};
// ๋ก๊ทธ์ธ
export const loginUser = async ({ email, password }, callback) => {
await setPersistence(auth, browserSessionPersistence);
return await signInWithEmailAndPassword(auth, email, password)
.then((res) => res)
.catch((error) => callback(`๋ก๊ทธ์ธ์ ์คํจํ์ต๋๋ค. ${error.code}`));
};
// ๋ก๊ทธ์์
export const logoutUser = () => {
signOut(auth);
};
// ์ ์ ๊ด์ฐฐ
export const onUserStateChange = (callback) => {
onAuthStateChanged(auth, (user) => {
callback(user);
});
};
AuthContext์์๋ user ์ธ์ฆ ์ ๋ณด๋ฅผ ๊ด๋ฆฌํ๊ณ , ์ธ์ฆ ์ ๋ณด๋ฅผ ์ด์ฉํด์ ํ์ฌ ๋ก๊ทธ์ธ ์ค์ธ์ง ์๋์ง ์ฌ๋ถ๋ฅผ ์๋ ค์ค๋ค. context ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๊ธฐ ์ํ useContext ์ฝ๋๋ฅผ useAuthContext
๋ก ๋ง๋ค์ด์ ์ปดํฌ๋ํธ์์๋ ์ต๋ํ ๊ฐํธํ๊ฒ context ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ฌ ์ ์๋๋ก ํ๋ค.
import { createContext, useContext, useEffect, useState } from "react";
import { onUserStateChange } from "../api/firebase";
const AuthContext = createContext({
user: {},
isLoggedIn: false,
});
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState();
const contextValue = {
user,
isLoggedIn: !!user,
};
useEffect(() => {
onUserStateChange((user) => setUser(user));
}, []);
return (
<AuthContext.Provider value={{ ...contextValue }}>
{children}
</AuthContext.Provider>
);
};
export function useAuthContext() {
return useContext(AuthContext);
}
์คํ๋ก์ธํด email์ ๋ณด๋ฅผ ๋ฐ์์ค์ง ๋ชปํด์ ๋ฐ์ํจ. ์คํ ์์ ํ ํด๊ฒฐ
์ปดํฌ๋ํธ์์ api๋ฅผ ์ง์ ํธ์ถ ํ ๊ฒ์ธ์ง, api๋ฅผ ๊ด๋ฆฌํ๋ context๋ redux๋ฅผ ๋ง๋ค์ด ๊ฐ์ ํธ์ถ ํ ๊ฒ์ธ์ง ๊ณ ๋ฏผํ๋ค. ์ผ๋จ์ auth api๋ SignForm
์ปดํฌ๋ํธ์์๋ง ์ฌ์ฉ๋๋ฏ๋ก ๊ตณ์ด context, redux๋ ์ฌ์ฉํ์ง ์๊ธฐ๋ก ํ๊ณ , user ์ธ์ฆ ์ ๋ณด๋ ํ๋ก์ ํธ ์ ์ญ์ ์ผ๋ก ํ์ํ ๋ฐ์ดํฐ์ด๊ธฐ ๋๋ฌธ์ context๋ก ๊ด๋ฆฌํ๋ ๋ฐฉํฅ์ผ๋ก ์งํํ๋ค.
๊ทธ๋ฐ๋ฐ ์ด๋ ๊ฒ ํ๋ SignForm ์ปดํฌ๋ํธ ์ฝ๋๊ฐ ์ข ๋๋ฌ์๋ณด์ธ๋ค. ์ฐ์ ์ ์ด๋ ๊ฒ ๋๊ณ ํ์ ๋ฆฌํฉํ ๋ง์ ๊ณ ๋ฏผํด ๋ด์ผ๊ฒ ๋ค.
firebase api๋ฅผ ์ฌ์ฉํ๋๋ '๋ก๊ทธ์์' api๋ฅผ ์คํํ์ง ์์ผ๋ฉด user ์ ๋ณด๊ฐ ์๊ตฌ์ ์ผ๋ก ์กด์ฌํ๋ ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ๋ค. ์ํฉ์๋ฐ๋ผ ๋ฌธ์ ๊ฐ ์๋ ์ ์๊ฒ ์ง๋ง ๋๋ ๋ธ๋ผ์ฐ์ ๊ฐ ๋ซํ๋ฉด ์๋์ผ๋ก ๋ก๊ทธ์์ ๋๊ณ user ์ ๋ณด๊ฐ ์ญ์ ๋๊ธฐ๋ฅผ ๋ฐ๋ฌ๋ค.
firebased์์ ๊ด๋ จ ์ค์ ์ ๋ณ๊ฒฝํ ์ ์๋ api๋ฅผ ์ ๊ณตํ๋๋ฐ ์ด ์ธ๊ฐ์ง ์ง์ ์ ํ์ด ์๋ค.
1. LOCAL : '๋ก๊ทธ์์'์ ํ์ง ์๋ ์ด์ ๊ณ์ ์ง์.
2. SESSION : ์ธ์ฆ ์ํ๊ฐ ์ธ์
์ ํํด์ ์ง์๋๊ณ ํญ์ด๋ ๋ธ๋ผ์ฐ์ ๊ฐ ๋ซํ๋ฉด ์ญ์ ๋จ.
3. NONE: ์ํ๊ฐ ๋ฉ๋ชจ๋ฆฌ์๋ง ์ ์ฅ๋๊ณ ์๋ก ๊ณ ์นจํ๋ฉด ์ญ์ ๋จ.
๋๋ ์ธ์ ๋ฐฉ์์ ์ ํํด์ ์ํ๋๋๋ก ๋์์ํฌ ์ ์์๋ค.