로그인과 회원가입은 어떻게 이루어질까?!
백과 프론트의 협업이 잘 이루어져야 하는 부분이 로그인/회원가입입니다.
사용자의 개인정보를 다루어야 하기 때문에 보안이 매우 중요하고 사용자에 따른 접근 권한과 특정 리소스에만 접근 가능하게 하는 과정이 하나의 서비스에서 필수적인 부분이라 그렇습니다.
프론트에서는 로그인/회원가입을 어떻게 진행하는지 살펴보도록 하겠습니다!
import { Link } from 'react-router-dom'
import { useContext } from 'react'
import { AuthContext } from '../../store/auth-context'
import classes from './MainNavigation.module.css'
const MainNavigation = () => {
const authCtx = useContext(AuthContext)
const isLoggedIn = authCtx.isLoggedIn
return (
<header className={classes.header}>
<Link to="/">
<div className={classes.logo}>React Auth</div>
</Link>
<nav>
<ul>
{!isLoggedIn && (
<li>
<Link to="/auth">Login</Link>
</li>
)}
{isLoggedIn && (
<li>
<Link to="/profile">Profile</Link>
</li>
)}
{isLoggedIn && (
<li>
<button>Logout</button>
</li>
)}
</ul>
</nav>
</header>
)
}
export default MainNavigation
import classes from './ProfileForm.module.css'
import { useContext, useRef } from 'react'
import AuthContext from '../../store/auth-context'
const ProfileForm = () => {
const newPasswordInputRef = useRef()
const authCtx = useContext(AuthContext)
const submitHandler = (event) => {
event.preventDefault()
const entredNewPassword = newPasswordInputRef.current.value
// add validation
fetch(
'https://identitytoolkit.googleapis.com/v1/accounts:update?key=AIzaSyCDF7hXMozn3Eq-mRlgmUH8It12KIFGh9Y',
{
method: 'POST',
body: JSON.stringify({
idToken: authCtx.token,
password: entredNewPassword,
returnSecureToken: false
}),
headers: {
'Content-Type': 'application/json'
}
}
).then((res) => {
// assumption: Always succeeds!
})
}
return (
<form className={classes.form} onSubmit={submitHandler}>
<div className={classes.control}>
<label htmlFor="new-password">New Password</label>
<input type="password" id="new-password" minLength="6" ref={newPasswordInputRef} />
</div>
<div className={classes.action}>
<button>Change Password</button>
</div>
</form>
)
}
export default ProfileForm
const logoutHandler = () => {
setToken(null)
}
import { useContext } from 'react'
import { Switch, Route, Redirect } from 'react-router-dom'
import Layout from './components/Layout/Layout'
import UserProfile from './components/Profile/UserProfile'
import AuthPage from './pages/AuthPage'
import HomePage from './pages/HomePage'
import AuthContext from './store/auth-context'
function App() {
const authCtx = useContext(AuthContext)
return (
<Layout>
<Switch>
<Route path="/" exact>
<HomePage />
</Route>
{!authCtx.isLoggedIn && (
<Route path="/auth">
<AuthPage />
</Route>
)}
<Route path="/profile">
{authCtx.isLoggedIn && <UserProfile />}
{!authCtx.isLoggedIn && <Redirect to="/auth" />}
</Route>
<Route path="*">
<Redirect to="/" />
</Route>
</Switch>
</Layout>
)
}
export default App
import React, { useState } from 'react'
export const AuthContext = React.createContext({
token: '',
isLoggedIn: false,
login: (token) => {},
logout: () => {}
})
export const AuthContextProvider = (props) => {
const initialToekn = localStorage.getItem('token')
const [token, setToken] = useState(initialToekn)
// !! -> truthy falsy 값을 ture나 false인 불리언 값으로 바꿔줌
// token이 빈 문자열이 아니라면 -> true로 바꿔줌, 빈 문자열이라면 -> false로 바꿔줌
console.log(token)
const userIsLoggedIn = !!token
const loginHandelr = (token) => {
setToken(token)
localStorage.setItem('token', token)
}
const logoutHandler = () => {
setToken(null)
localStorage.removeItem('token')
}
const contextValue = {
token: token,
isLoggedIn: userIsLoggedIn,
login: loginHandelr,
logout: logoutHandler
}
return <AuthContext.Provider value={contextValue}>{props.children}</AuthContext.Provider>
}
export default AuthContext
const calculateRemainingTime = (expirationTime) => {
// 현재 시간
const currentTime = new Date().getTime()
// 만료 시간
const adjExpirationTime = new Date(expirationTime).getTime()
// 남은 시간
const remainingDuration = adjExpirationTime - currentTime
return remainingDuration
}
.then((data) => {
// 현재 시간 기준 만료시간 구하기(Date 객체로 만둘어줌)
const expirationTime = new Date(new Date().getTime() + +data.expiresIn * 1000)
authCtx.login(data.idToken, expirationTime.toISOString())
// 사용자를 다른 페이지로 리다이렉션, replace -> 뒤로가기 버튼X
history.replace('/')
})