리액트가 로그인 요청
익스프레스가 요청을 받아서 mysql에서 존재하는지 확인
존재하면 jwt 토큰을 생성해서(시간제약 가능) 리액트에게 전달
리액트는 jwt 토큰을 받아서
📌 로그인 된상태: localstorage 에 jwt 토큰이 존재하는 상태
전역 state 변수에도 저장되어 있음
📌 로그인 안된 상태: localstorage 와 전역 state변수에 jwt 토큰이 저장되지 않는 상태
📌 토큰이 저장되었지만 만료된상태(로그인 시간이 오래되어서)
📌 대시보드 레이아웃은 로그인 한 사람만 봐야한다 (권한존재)
📝 "?" 물음표 이후는 "변수"
**리액트에서**
'/api/users' GET요청하면 👉 회원전체 조회야
**익스프레스 주소**
'/api/users?lastName=김' 회원 김씨 조회야 하고 👉 김씨인사람만 조회, 응답함.
//ex) get요청 검색을 할경우 (search로 get요청) 안녕을 검색할경우 -> 물음표 뒤에 "?q=안녕"
**리액트에서**
'/api/users?lastName=김' GET요청하면,(회원전체조회하고싶은데 성씨가 김씨인사람만 전체조회하고싶다.)
**익스프레스 주소**
'/api/users?lastName=강' 회원 강씨 조회야 하고 👉 강씨인사람만 조회, 응답함.
📌 이렇게 하나씩 다 쓸거야????? =-> NO! 이건 너무 비효율적이야
'/api/users?lastName=김&firstName=철수' GET요청하면 👉 김철수인사람만 조회
/api/users/abc@naver.com?aaa=김철수
/api/users/test@naver.com
/api/users/:email
/api/users?email=abc@naver.com GET요청의 경우에는 주소에다가 데이터를 넘긴다
둘중에 무엇을사용해야하나? 👉 취향차이
login.js
const { setAccessToken } = useContext(UserContext);
header 는 객체이다 (key 와 value가 있다)
그중에서 Authorization 키값이 있어서 거기에 토큰을 넣어 전달할것이다.
토큰을 전역변수에도 넣어 저장하고 , 로컬스토리지에도 해놓음.
헤더도 객체
주소로 보내지않고 헤더로 받아온다
헤더같은 경우는 정보를 받아온다.
토큰을 보내줄때 토큰을 그냥 보내지않고 앞에 암호처럼 Bearer을 붙여서온다 (이건 그냥 이렇게 정해진거다)
header.js
import { useContext, useEffect } from "react";
import { UserText } from "../../styles/common/aside.styles";
import { Header } from "../../styles/common/header.styles";
import axios from "axios";
import { UserContext } from "../../App";
const DashboardHeader = () => {
//전역state 변수에 있는 토큰 값 가져오기 (login.js에서 설정한 accessToken)
const { accessToken } = useContext(UserContext);
//header가 그려지면 db가서 로그인 한 사람 정보 가져오기
useEffect(() => {
axios.get('/api/loggedInEmail',
{ headers: { Authorization: `Bearer ${accessToken}` }});
// header안에 문자열을 전달해줘 (Bearer 뒤에 한칸 띄어쓰기)
// axios.get('/api/users/로그인한사람id')
}, []);
return (
<Header>
<UserText>강수지<span>님</span></UserText>
<i class="fa-solid fa-bars menu-icon"></i>
</Header>
);
}
export default DashboardHeader;
이렇게 Bearer에 null 이 들어간것을 볼수있다.
지금 현재는 accecss token는 null 이기때문에.
새로고침하면 사라지기때문에 null이 나옴
header.js
useEffect(() => {
axios.get('/api/loggedInEmail',
{ headers: { Authorization: `Bearer ${localStorage.getItem(accessToken)}` }});
app.js
const jwt = require("jsonwebtoken");
//token을 전달받아서 로그인한 사람의 email 주소를 되돌려주는 api
app.get('/api/loggedInEmail', (req, res) => {
//리액트로부터 전달받은 토큰이 정삭적인지 확인하고
//정상적이지 않으면 오류로 응답
//정상적이면 email주소로 응답
//토큰은 요청 header의 Authorization에 Bearer 토큰값
// console.log(req.headers.authorization)
// 문자열로 받음
const token = req.headers.authorization.replace('Bearer ', '');
// console.log(token);
// token은 로그인 당시 발급 받은 토큰
try{
let result = jwt.verify(token, process.env.JWT_SECRET);
// console.log(result);
res.send(result.email);
}catch(err){
console.log(err);
res.status(403).json('오류발생!');
}
});
.env.local
설정해주고,
JWT_SECRET=board23
🌟 console.log(req.headers.authorization)
👇
결과값?
<const token = req.headers.authorization.replace('Bearer ', ''); //순수 토큰 발급됨 앞에 Bearer 빠지고 ->관리차원에서 이렇게 나옴.
replace() 함수는 문자열에서 특정 문자 또는 패턴을 찾아 다른 문자열로 대체할 때 사용됩니다
let originalString = "Hello, World! Hello, Universe!";
let newString = originalString.replace("Hello", "Hi");
//hello를 hi로 바꿔줘
console.log(newString);
//"Hi, World! Hello, Universe!"
🔎 header가 그려지면 db가서 로그인 한 사람 정보 가져오기 (전역state 변수에 있는 토큰 값 가져오기 )
header.js
import { useContext, useEffect } from "react";
import { UserText } from "../../styles/common/aside.styles";
import { Header } from "../../styles/common/header.styles";
import axios from "axios";
import { UserContext } from "../../App";
const DashboardHeader = () => {
//전역state 변수에 있는 토큰 값 가져오기 (login.js에서 설정한 accessToken)
const { accessToken } = useContext(UserContext);
//header가 그려지면 db가서 로그인 한 사람 정보 가져오기
useEffect(() => {
axios.get('/api/loggedInEmail', { headers: { Authorization: `Bearer ${accessToken}` } }); // header안에 문자열을 전달해줘 (Bearer 뒤에 한칸 띄어쓰기)
// axios.get('/api/users/로그인한사람id')
}, []);
return (
<Header>
<UserText>강수지<span>님</span></UserText>
<i class="fa-solid fa-bars menu-icon"></i>
</Header>
);
}
export default DashboardHeader;
app.js
2:20
app.get('/api/users/:email', async (req, res) => { //:email 이라고 쓰면 email이라는 변수로 사용가능 (동적으로 요청)
// console.log(req.params);
const email = req.params.email;
let sql = `
SELECT email, created_date, updated_date, profile_url, cover_url
from tbl_users
WHERE email = ?
`;
try {
let [rows, fields] = await pool.query(sql, [email]);
res.json(rows[0]);
} catch (err) {
res.status(500).json('서버 오류 발생');
}
});
rows[0]->객체로 받아서 보내준다.
header.js
import { useContext, useEffect, useState } from "react";
import { Header } from "../../styles/common/header.styles";
import axios from "axios";
import { UserContext } from "../../App";
import { useNavigate } from "react-router-dom";
const DashboardHeader = () => {
const navigate = useNavigate();
// 전역state변수에 있는 토큰 값 가져오기
const { accessToken } = useContext(UserContext);
const [loggedInEmail , setLoggedInEmail] = useState('로그인후 이용해주세요');
// 헤더가 그려지면 db가서 로그인 한 사람 정보 가져오기
useEffect(() => {
let tmp = async () => {
try {
let res = await axios.get('/api/loggedInEmail',
{ headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` } }
);
// res.data에 로그인한 사람 이메일 주소가 들어있음
let res2 = await axios.get(`/api/users/${res.data}`);
//console.log(res2.data);
setLoggedInUser(res2.data);
} catch (err) {
// 로그인 시간이 만료되거나 로그인을 안한 경우
console.log(err);
alert('로그인을 먼저 해주셔야 이용하실 수 있습니다');
navigate('/login', { replace: true });
}
}
tmp();
// axios.get('/api/users/로그인한사람id');
}, [navigate]);// 의존성으로 넣어줘야함
return (
<Header>
<UserText>{loggedInEmail}<span>님</span>
</UserText>
</Header>
);
}
export default DashboardHeader;
import { useContext, useEffect, useState } from "react";
import { UserText } from "../../styles/common/aside.styles";
import { Header } from "../../styles/common/header.styles";
import axios from "axios";
import { UserContext } from "../../App";
import { useNavigate } from "react-router-dom";
import MenuIcon from '@mui/icons-material/Menu';
import MenuOpenIcon from '@mui/icons-material/MenuOpen';
import { Popover } from "@mui/material";
const DashboardHeader = () => {
const navigate = useNavigate();
// 전역state변수에 있는 토큰 값 가져오기
const {accessToken, setAccessToken} = useContext(UserContext);
const [loggedInUser, setLoggedInUser] = useState({
email: '로그인후 이용해주세요',
created_date: '',
updated_date: ''
});
// const [loggedInEmail , setLoggedInEmail] = useState('로그인후 이용해주세요');
// popOver 창의 기준 element 요소
const [anchorEl, setAnchorEl] = useState(null);
let open = Boolean(anchorEl);
// 헤더가 그려지면 db가서 로그인 한 사람 정보 가져오기
useEffect(() => {
let tmp = async () => {
try {
let res = await axios.get('/api/loggedInEmail',
{ headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` } }
);
// res.data에 로그인한 사람 이메일 주소가 들어있음
let res2 = await axios.get(`/api/users/${res.data}`);
console.log(res2.data);
setLoggedInUser(res2.data);
} catch (err) {
// 로그인 시간이 만료되거나 로그인을 안한 경우
console.log(err);
alert('로그인을 먼저 해주셔야 이용하실 수 있습니다');
navigate('/login', { replace: true });
}
}
tmp();
// axios.get('/api/users/로그인한사람id');
}, [navigate]);
const onLogout = () => {
//로그아웃 버튼이 클릭되면 토큰을 삭제
//localStorage 에 저장된 토큰을 삭제
localStorage.removeItem('accessToken');
//전역 상태변수에 저장된 토큰도 삭제
setAccessToken(null);
navigate('/login', { replace: true });
}
return (
<Header>
<div>
<UserText onClick={(e) => { setAnchorEl(e.currentTarget) }}>{loggedInUser.email}<span>님</span></UserText>
<Popover
anchorEl={anchorEl}
open={open}
onClose={() => { setAnchorEl(null) }}
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
>
<p>회원가입일 : {loggedInUser.created_date}</p>
<p>마지막수정일: {loggedInUser.updated_date}</p>
<button onClick={onLogout}>로그아웃</button>
</Popover>
</div>
<MenuOpenIcon />
</Header>
);
}
export default DashboardHeader;
import { useContext, useState } from "react";
import { UserContext } from "../../App";
import { useNavigate } from "react-router-dom";
const {accessToken, setAccessToken} = useContext(UserContext);
const onLogout = () => {
//로그아웃 버튼이 클릭되면 토큰을 삭제
//localStorage 에 저장된 토큰을 삭제
localStorage.removeItem('accessToken');
//전역 상태변수에 저장된 토큰도 삭제
setAccessToken(null);
navigate('/login', { replace: true });
}
return (
<Header>
<div>
<UserText onClick={(e) => { setAnchorEl(e.currentTarget) }}>{loggedInUser.email}<span>님</span></UserText>
<Popover
anchorEl={anchorEl}
open={open}
onClose={() => { setAnchorEl(null) }}
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
>
<p>회원가입일 : {loggedInUser.created_date}</p>
<p>마지막수정일: {loggedInUser.updated_date}</p>
<button onClick={onLogout}>로그아웃</button>
</Popover>
</div>
<MenuOpenIcon />
</Header>
);
}