⚙ 로직
- App.js 단에서 token 확인
- token값 없을시 login page로 이동,passport js를 통해 token 발급 => 쿠키에 저장
- App.js단에서 쿠키값을 읽어오고, global state로 관리하여 전 페이지와 공유
- route로 해당 페이지에 접근시 state값에 저장된 토큰을 이용하여 API에 접근
❗ 문제
- route로 이동하는 페이지에서 새로고침시에, state값 초기화로 token 값 읽어오지못함
💡 해결법
- 초기화 함수를 통해서 새로고침이나 리다이렉션되는 상황에도 상태값이 유지될 수있도록 한다.
참고 블로그 1
참고 블로그 2
🔎 소스코드
import './App.css';
import React, {createContext, useEffect,useLayoutEffect,useState} from 'react';
import {BrowserRouter, Routes, Route } from 'react-router-dom';
import { Cookies } from 'react-cookie';
// components
import Main from './views/Main'
import Edit from './views/Edit';
import Post from './views/Post';
import Login from './views/Login';
export const AppContext = createContext();
function App() {
const cookies = new Cookies();
const [login, setLogin] = useState({
id:null,
password:null,
})
const [isLogin, setIsLogin] = useState(false)
const [token, setToken] = useState(cookies.get('token'))
async function checkAuth() {
let userInfo = {
userId: login.id,
userPw:login.password
}
await fetch(process.env.REACT_APP_SERVER_ADDRESS+process.env.REACT_APP_AUTH, {
method:'POST',
mode:'cors',
credentials: 'include',
headers:{
'Content-Type' : 'application/json',
'Accept' : 'application/json',
'Access-Control-Allow-Origin':'*',
},
body: JSON.stringify(userInfo)
})
.then(res=>{
return res.json();
})
.then(data=>{
console.log('data',data)
setToken(data.accessToken)
})
.catch(err=>{
setToken(null)
})
}
const onSaveInfo=(e)=>{
console.log(e.target.value)
let target = e.target.className;
let values = e.target.value;
if(target==='login_id'){
setLogin({
...login,
id:values
})
}else {
setLogin({
...login,
password:values
})
}
}
function getCookie() {
return cookies.get('token')
}
useEffect(()=>{
if(token!==null){
setIsLogin(true)
}
},[token])
useLayoutEffect(()=>{
let temp = getCookie();
if(temp!==undefined) {
setToken(temp)
}
},[])
return (
<AppContext.Provider value={token}>
<BrowserRouter>
<div className="App">
<div className='content'>
<Routes>
{!isLogin
?
<Route excact path="/" element={<Login onChange={onSaveInfo} onClick={checkAuth}/>}></Route>
:
<Route excact path="/" element={<Main/>}></Route>
}
<Route excact path="/" element={<Main/>}></Route>
<Route excact path="/login" element={<Login onChange={onSaveInfo} onClick={checkAuth}/>}></Route>
<Route path="/category/:id" element={<Main/>}></Route>
<Route path="/read/:id" element={<Post/>}></Route>
<Route path="/write" element={<Edit/>}></Route>
<Route path="/write/:id" element={<Edit/>}></Route>
</Routes>
</div>
</div>
</BrowserRouter>
</AppContext.Provider>
);
}
export default App;
- Main.js (./src/views/Main.js)
import React,{useState, useEffect, useLayoutEffect, useContext} from "react";
import { useNavigate ,useParams,useLocation, redirect } from "react-router-dom";
import styled from "styled-components";
import moment from "moment/moment";
import { Cookies } from 'react-cookie';
// components
import Button from "../components/Button";
import Header from "../components/Header";
// svgs
import Edit from "../public/images/edit.png"
import Delete from "../public/images/delete.png"
import New from '../public/images/new.png'
import { AppContext } from "../App";
export default function Main(props){
let location = useLocation();
let navigate = useNavigate();
const token = useContext(AppContext);
const [isLogin, setIsLogin] = useState(false)
const [posts, setPosts] = useState([])
const [paramId, setParamId] = useState()
const [btnDetail, setBtnDetail] = useState({
title:'',
url:''
})
const onClickPost=(e,num)=>{
let url = num;
navigate(`/read/${url}`, {
state: {
post_num: url
}
})
}
const onDeletePost=(e,num)=>{
fetch(process.env.REACT_APP_SERVER_ADDRESS+process.env.REACT_APP_ACCESS_DEL+`/${num}`, {
mode:'cors',
method:'PATCH',
headers:{
'Content-Type' : 'application/json',
'Accept' : 'application/json',
'Access-Control-Allow-Origin':'*',
'Authorization':'Bearer ' + token,
}
})
.then(res=> {
console.log(res,'resresee')
return res.json();
})
.then((data)=>{
if(data.code===200) {
alert('삭제 완료')
}
})
.catch((err)=>
console.log(err)
);
}
function checkLogin() {
console.log(token)
if(token!==null) {
// if(token==null) {
setBtnDetail({
...btnDetail,
title:'글 작성하기',
url:'/write'
})
setIsLogin(true)
}else{
setBtnDetail({
...btnDetail,
title:'로그인하기',
url:'/login'
})
}
}
function getLists() {
fetch(paramId === undefined ? process.env.REACT_APP_SERVER_ADDRESS+process.env.REACT_APP_ACCESS_BOARD : process.env.REACT_APP_SERVER_ADDRESS+process.env.REACT_APP_CATEGORY+'/'+paramId, {
mode:'cors',
headers:{
'Accept' : 'application/json',
'Access-Control-Allow-Origin':'*',
'Authorization':'Bearer ' + token,
}
})
.then(res=> {
console.log(res)
if(res.statusText==='Unauthorized') {
alert('인증이 필요합니다.')
window.location.replace('/')
}
return res.json();
})
.then(data=>{
setPosts(data)
})
.catch((err)=>
console.log(err)
);
}
useLayoutEffect(()=>{
if(location.pathname==='/') {
setParamId(undefined)
}else {
setParamId(location.state.category)
}
checkLogin()
getLists()
},[])
useEffect(()=>{
getLists()
},[paramId])
return(
<MainStyle>
<div className="mainWrapper">
{/* <div className="mainTop">게시판</div> */}
<div className="contents">
<Header isShown={true} title={btnDetail.title} url={btnDetail.url} onReadUrl={setParamId}/>
<div className="categoryTitle">{paramId === undefined ? '전체보기': paramId}</div>
<table className="lists">
<tr >
<th>번호</th>
<th>제목</th>
<th>글쓴이</th>
<th>작성시간</th>
<th>조회수</th>
</tr>
{
posts.map((post,index)=>{
return(
<tr key={`list`+index} name={post.num} onClick={(e)=>onClickPost(e,post.num)}>
<td>{(posts.length)-(index)}</td>
<td>{post.title}</td>
<td>{post.writer}</td>
<td>{moment(post.insert_date).format('YYYY-MM-DD HH:mm:ss')}</td>
<td>{post.hits}</td>
</tr>
)
})
}
</table>
<input type='button' onClick={(e)=>{logout(e)}} value='로그아웃'></input>
</div>
</div>
</MainStyle>
)
}