์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- cmarket
- redux์ํ์ ์ง
- removeCookie
- @redux-toolkit
- ํท๊ฐ๋ฆฐ๋ค~
- ใทใ
- for~in/for~of
- toString#String
- UX
- https://www.daleseo.com/js-array-slice-splice/
- User Flow
- ๋ ธ๋๊ต๊ณผ์
- https://lo-victoria.com/introduction-to-redux-toolkit-for-beginners
- ์๋ฐ์คํฌ๋ฆฝํธ#JS#var#let#const#undefined#null
- ์๋ฐ์คํฌ๋ฆฝํธ#์กฐ๊ฑด๋ฌธ#๋ฌธ์์ด
- dom
- Beesbeesbees
- ๋ด์ฅ๊ณ ์ฐจํจ์
- variable#function
- ์๋ฐ์คํฌ๋ฆฝํธ#JS#slice#splice
- js
- https://dasima.xyz/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%A0%9C%EA%B3%B1-math-pow-%EA%B3%84%EC%82%B0/
- https://developer-talk.tistory.com/299
- CSS
- UI
- slice/splice/split
- children vs childrenNodes
- ์๋ฐ์คํฌ๋ฆฝํธ
- JS#3์ผ์ฐจ๋ฌ๋ฆฌ์#์ด๋ฐ์ธ๋ฐ#์๊ฐ๊ธ๋ฐฉ~
- react
- Today
- Total
Daily Front_Minhhk
[cookie] sprint-auth-cookie ๋ณธ๋ฌธ
๐คช ์ด๋ป๊ฒ ์ฐพ์ผ์ ๋ถ์๊ฒ ๋์์ด ๋๊ธธ,, ์ด๋ ค์์ ๊ณ ์ํ์ต๋๋ค. ์์ฑ ๋ถ๋ถ ์ ๋๋ก ๊ฐ๋ตํ!!
๊ณผ์ ์ ์ด๋ ๋ค!
๐ก ์๋ฒ์ด๊ธฐ
HTTPS ํ๋กํ ์ฝ์ ์ฌ์ฉํ ์ ์๋๋ก
์๋ฒ์ index.js ํ์ผ์ ํ์ธํด๋ณด๋ฉด ์ธ์ฆ์๋ฅผ ๋ถ๋ฌ์ค๋ ์ฝ๋๊ฐ ์๋๋ฐ,
// ์ธ์ฆ์ ํ์ผ๋ค์ด ์กด์ฌํ๋ ๊ฒฝ์ฐ์๋ง https ํ๋กํ ์ฝ์ ์ฌ์ฉํ๋ ์๋ฒ๋ฅผ ์คํํฉ๋๋ค.
// ๋ง์ฝ ์ธ์ฆ์ ํ์ผ์ด ์กด์ฌํ์ง ์๋๊ฒฝ์ฐ, http ํ๋กํ ์ฝ์ ์ฌ์ฉํ๋ ์๋ฒ๋ฅผ ์คํํฉ๋๋ค.
// ํ์ผ ์กด์ฌ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ํด๋๋ package.json์ด ์์นํ server ํด๋์
๋๋ค.
let server;
if (fs.existsSync("./key.pem") && fs.existsSync("./cert.pem")) {
const privateKey = fs.readFileSync(__dirname + "/key.pem", "utf8");
const certificate = fs.readFileSync(__dirname + "/cert.pem", "utf8");
const credentials = {
key: privateKey,
cert: certificate,
};
ํด๋น ํด๋์ ์ธ์ฆ์๊ฐ ์๊ธฐ ๋๋ฌธ์ HTTPS ์๋ฒ๊ฐ ์๋ ๊ทธ๋ฅ HTTP ์๋ฒ๊ฐ ์ด๋ ค์
- ์๋ฒ ํด๋ ์์์ mkcert๋ฅผ ์ฌ์ฉํด ์ธ์ฆ์๋ฅผ ๋ง๋ค์ด์ HTTPS ์๋ฒ๋ก ๋ณ๊ฒฝ
- mkcert -key-file key.pem -cert-file cert.pem localhost 127.0.0.1 ::1
๋๋จธ์ง ๊ฑด๋ค์ง ๋ง๊ณ corsOptions ์ Cors ์ค์ ํด์ค๋ค.
server/index.js
const corsOptions = {
/* TODO: CORS ์ค์ ์ด ํ์ํฉ๋๋ค. ํด๋ผ์ด์ธํธ๊ฐ ์ด๋ค origin์ธ์ง์ ๋ฐ๋ผ ๋ฌ๋ฆฌ ์ค์ ํ ์ ์์ต๋๋ค.
* ๋ฉ์๋๋ GET, POST, OPTIONS๋ฅผ ํ์ฉํฉ๋๋ค.
*/
// client๋ <http://localhost:3000> ์ ์ด์ฉํ๊ฒ ๋ฉ๋๋ค.
origin: "<http://localhost:3000>",
// cookie๋ ์ธ์ฆ ์ ๋ณด๋ฅผ ํฌํจํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ผ๋ฏ๋ก credentials๋ ์ค์ ํด์ค๋๋ค.
credentials: true,
// ํ์ฉํ ๋ฉ์๋๋ฅผ ๋ฐฐ์ด์ ๋ด์์ ์์ฑํด์ค๋๋ค.
methods: ["GET", "POST", "OPTION"],
};
app.use(cors(corsOptions));
app.post("/login", controllers.login);
app.post("/logout", controllers.logout);
app.get("/userinfo", controllers.userInfo);
ํน์ ํฌ๋กฌ https ์๋ฌ๊ฐ ๋ฌ๋ค๋ฉด
→
ํฌ๋กฌ ์ ๋ฐ์ดํธ ํด์ค์๋ค(์๋ ์ด์ ์ ๋/)
์๋ ๋งํฌ์ ๋ ์์ธํ ์ค๋ช !
์ฐธ์กฐ
๐ช Client
client/app.js
→ ์ ๋ฌ ํด ์ค props ๋ฐ์ ๋ด๋ ค์ค๊ธฐ
import "./App.css";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Login from "./pages/Login";
import Mypage from "./pages/Mypage";
import React, { useEffect, useState } from "react";
import axios from "axios";
// ๋ชจ๋ ์์ฒญ์ withCredentials๊ฐ true๋ก ์ค์ ๋ฉ๋๋ค.
axios.defaults.withCredentials = true;
function App() {
const [isLogin, setIsLogin] = useState(false);
const [userInfo, setUserInfo] = useState(null);
const authHandler = () => {
return axios
.get("<https://localhost:4000/userinfo>")
.then((res) => {
setIsLogin(true);
setUserInfo(res.data);
})
.catch((err) => {
console.log(err.response.data);
});
};
useEffect(() => {
// ์ปดํฌ๋ํธ ์์ฑ ์ ์๋ ํจ์๊ฐ ์คํ๋ฉ๋๋ค.
authHandler();
}, []);
return (
<BrowserRouter>
<div className="main">
<Routes>
<Route
path="/"
element={
isLogin ? (
<Mypage
/*
TODO: ๋ ๋๋ง์ ํ์ํ App์ ์ํ์ ์ด๋ฅผ ํ์ ์ปดํฌ๋ํธ์์ ๋ณ๊ฒฝํ ์ ์๋๋ก props๋ฅผ ์ ๋ฌํ์ธ์.
*/
userInfo={userInfo}
setIsLogin={setIsLogin}
setUserInfo={setUserInfo}
/>
) : (
<Login
/*
TODO: App์ ์ํ๋ฅผ ๋ณ๊ฒฝํ ์ ์๋๋ก props๋ฅผ ์ ๋ฌํ์ธ์.
*/
setIsLogin={setIsLogin}
setUserInfo={setUserInfo}
/>
)
}
/>
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
client/pages/Login.js
→ props๋ก ์ ๋ฌ ๋ฐ์ ๊ฒ ์ ์ฉ
→ ๋ก๊ทธ์ธ ๋ฒํผ์ ๋๊ฒจ์ค ํธ๋ค๋ฌ
export default function Login({ setIsLogin, setUserInfo }) {
const [loginInfo, setLoginInfo] = useState({
userId: "",
password: "",
});
const [checkedKeepLogin, setCheckedKeepLogin] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const handleInputValue = (key) => (e) => {
setLoginInfo({ ...loginInfo, [key]: e.target.value });
};
const loginRequestHandler = () => {
// TODO: Login ์ปดํฌ๋ํธ๊ฐ ๊ฐ์ง๊ณ ์๋ state๋ฅผ ์ด์ฉํด ๋ก๊ทธ์ธ์ ๊ตฌํํฉ๋๋ค.
// ๋ก๊ทธ์ธ์ ํ์ํ ์ ์ ์ ๋ณด๊ฐ ์ถฉ๋ถํ ์ ๊ณต๋์ง ์์๋ค๋ฉด ์๋ฌ๋ฉ์์ง๊ฐ ๋ํ๋๋๋ก ๊ตฌํํ์ธ์.
if (!loginInfo.userId || !loginInfo.password) {
setErrorMessage("์์ด๋์ ๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํ์ธ์");
// ์
๋ ฅ๋์ง ์์ ๊ฐ์ด ์๋๊ฑฐ๋๊น ์์ฒญ์ ๋ณด๋ด๋ณผ ํ์๋ ์์ด ๋ฐ๋ก ๋ฆฌํดํด์ค๋๋ค.
return;
}
return axios
.post("<https://localhost:4000/login>", { loginInfo, checkedKeepLogin })
.then((res) => {
// ๋ก๊ทธ์ธ์ ์ฑ๊ณตํ๋ค๋ฉด ์๋ต์ผ๋ก ๋ฐ์ ๋ฐ์ดํฐ๊ฐ Mypage์ ๋ ๋๋ง๋๋๋ก State๋ฅผ ๋ณ๊ฒฝํ์ธ์.
setIsLogin(true);
setUserInfo(res.data);
//์ฌ๊ธฐ์์ ์๋ฌ ์ด๊ธฐํ
setErrorMessage("");
})
.catch((err) => {
// ๋ก๊ทธ์ธ์ ์คํจํ๋ค๋ฉด ๊ทธ์ ๋ํ ์๋ฌ ํธ๋ค๋ง
if (err.response.status === 401) {
setErrorMessage("๋ก๊ทธ์ธ์ ์คํจํ์ต๋๋ค.");
alert('๋ก๊ทธ์ธ์ ์คํจํ์ต๋๋ค!')
}
});
};
client/pages/Mypage.js
→ props๋ก ์ ๋ฌ ๋ฐ์ ๊ฒ ์ ์ฉ
→ ๋ก๊ทธ์์์ ๋ก๊ทธ์ธ๊ณผ ๋ฐ๋๋ก
โก๏ธ setIsLogin(false); setUserInfo(null);
export default function Mypage({ userInfo, setIsLogin, setUserInfo }) {
const logoutHandler = () => {
// TODO: Logout ๋ฒํผ์ ๋๋ ์ ์ Login ํ์ด์ง๋ก ๋์๊ฐ ์ ์๋๋ก ๊ตฌํํ์ธ์.
return axios
.post("<https://localhost:4000/logout>")
.then((res) => {
// ๋ก๊ทธ์์์ ์ฑ๊ณตํ๋ค๋ฉด App์ ์ํ๋ฅผ ๋ณ๊ฒฝํ์ธ์.
setIsLogin(false);
setUserInfo(null);
})
.catch((err) => {
// ๋ก๊ทธ์์์ ์คํจํ๋ค๋ฉด ๊ทธ์ ๋ํ ์๋ฌ ํธ๋ค๋ง์ ๊ตฌํํ์ธ์.
console.log(err.response.data);
alert(err);
});
};
npm test → ํ ์คํธ ํต๊ณผใฑใฑ
server/db/data.js ํ์ธ
→ ๋ก๊ทธ์ธ, ์์ ํ ๋ ์ฌ์ฉ!
ID : kimcoding
PASSWORD : 1234
๐ช Server
index.js ๋ ๋งจ ์ฒซ๋ถ๋ถ ์๋ฒ ์ด๊ธฐ์์ ํ์ธ!
์! ๋ค์
server/controllers/users/login.js
const { USER_DATA } = require("../../db/data");
module.exports = (req, res) => {
const { userId, password } = req.body.loginInfo;
const { checkedKeepLogin } = req.body;
const userInfo = {
// ์๋ณธ ๊ฐ์ฒด ๋ฐ์ดํฐ ์ ์ง ์ํด ๋ณต์ฌ๋ณธ ์ฌ์ฉ
...USER_DATA.filter(
(user) => user.userId === userId && user.password === password
)[0],
};
//! ์ฝ์,,
// console.log(req.body);
// console.log(userInfo);
//! ์ฟ ํค์ต์
const cookiesOption = {
domain: "localhost",
path: "/",
httpOnly: true,
sameSite: "none",
secure: true,
//! 7์ผ ํ ์๋ฉธ๋๋ Persistent Cookie
expires: new Date(Date.now() + 24 * 3600 * 1000 * 7),
};
if (!userInfo.id) {
res.status(401).send("Not Authorized");
} else if (checkedKeepLogin) {
res.cookie("cookieId", userInfo.id, cookiesOption);
res.redirect("/userinfo");
} else {
// expires ์ต์
์ญ์ ํด์ค๋ค,, session, persistent cookie ๊ตฌ๋ณ ์ํด
delete cookiesOption.expires;
res.cookie("cookieId", userInfo.id, cookiesOption);
res.redirect("/userinfo");
}
/*
* TODO: ๋ก๊ทธ์ธ ๋ก์ง์ ๊ตฌํํ์ธ์.
*
* userInfo์๋ ์์ฒญ์ ๋ฐ๋๋ฅผ ์ด์ฉํด db์์ ์กฐํํ ์ ์ ์ ๋ณด๊ฐ ๋ด๊ฒจ์์ต๋๋ค. ์ฝ์์์ userInfo๋ฅผ ์ถ๋ ฅํด๋ณด์ธ์.
* ์ ์ ์ ์ ๋ณด๊ฐ ์ถ๋ ฅ๋๋ค๋ฉด ํด๋น ์ ์ ๊ฐ ์กด์ฌํ๋ ๊ฒ์์ผ๋ก ๋ก๊ทธ์ธ ์ฑ๊ณต์ ๋ํ ์๋ต์ ์ ์กํด์ผ ํฉ๋๋ค.
* ๋ง์ฝ undefined๊ฐ ์ถ๋ ฅ๋๋ค๋ฉด ํด๋นํ๋ ์ ์ ๊ฐ ์กด์ฌํ์ง ์๋ ๊ฒ์์ผ๋ก ๋ก๊ทธ์ธ ์คํจ์ ๋ํ ์๋ต์ ์ ์กํด์ผ ํฉ๋๋ค.
*
* ๋ก๊ทธ์ธ ์ฑ๊ณต ์์๋ ํด๋ผ์ด์ธํธ์ ์ฟ ํค๋ฅผ ์ ์กํด์ผํฉ๋๋ค. ์ฟ ํค์ cookieId์๋ userInfo.id๊ฐ ๋ด๊ฒจ์ผ ํฉ๋๋ค.
* ํ
์คํธ์ผ์ด์ค์์ ์๊ตฌํ๋ ์ฟ ํค ์ต์
์ ๋ชจ๋ ์ค์ ํ์ธ์.
* ์์์ฑ์๋ ์ฟ ํค๋ฅผ ๋ณด๋ด๋ ค๋ฉด max-age ๋๋ expires ์ต์
์ ์ค์ ํ์ธ์.
*
* ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐ๋ก ์๋ต์ ๋ณด๋ด์ง์๊ณ ์๋ฒ์ /useinfo๋ก ๋ฆฌ๋ค์ด๋ ํธํด์ผ ํฉ๋๋ค.
* express์ res.redirect ๋ฉ์๋๋ฅผ ์ฐธ๊ณ ํ์ฌ ์๋ฒ์ /userinfo๋ก ๋ฆฌ๋ค์ด๋ ํธ ๋ ์ ์๋๋ก ๊ตฌํํ์ธ์.
*/
};
server/controllers/users/logout.js
→
module.exports = (req, res) => {
/*
* TODO: ๋ก๊ทธ์์ ๋ก์ง์ ๊ตฌํํ์ธ์.
*
* cookie-parser์ clearCookie('์ฟ ํค์ ํค', cookieOption) ๋ฉ์๋๋ก ํด๋น ํค๋ฅผ ๊ฐ์ง ์ฟ ํค๋ฅผ ์ญ์ ํ ์ ์์ต๋๋ค.
* ๋ง์ฝ res.clearCookie('user', cookieOption) ์ฝ๋๊ฐ ์คํ๋๋ค๋ฉด `user=....` ์ฟ ํค๊ฐ ์ญ์ ๋ฉ๋๋ค.
* ๋ก๊ทธ์์ ์ฑ๊ณต์ ๋ํ ์ํ ์ฝ๋๋ 205๊ฐ ๋์ด์ผํฉ๋๋ค.
*/
const cookiesOption = {
domain: "localhost",
path: "/",
httpOnly: true,
sameSite: "none",
secure: true,
};
res.status(205).clearCookie("cookieId", cookiesOption).send("logout");
};
server/controllers/users/userInfo.js
→
const { USER_DATA } = require("../../db/data");
module.exports = (req, res) => {
/*
* TODO: ์ฟ ํค ๊ฒ์ฆ ์ฌ๋ถ์ ๋ฐ๋ผ ์ ์ ์ ๋ณด๋ฅผ ์ ๋ฌํ๋ ๋ก์ง์ ๊ตฌํํ์ธ์.
*
* ๋ก๊ทธ์ธ ์ ์ค์ ํ ์ฟ ํค๊ฐ ์กด์ฌํ๋ ์ง ํ์ธํด์ผ ํฉ๋๋ค.
* ์์ง ๋ก๊ทธ์ธ์ ํ์ง ์์๋ค๋ฉด ์ฟ ํค๊ฐ ์กด์ฌํ์ง ์์ ์ ์์ต๋๋ค.
* ์ฟ ํค์ ์ ์ ์ id๊ฐ ์กด์ฌํ๋์ง ํ์ธํ๊ณ ์ถ๋ค๋ฉด ์ฝ์์ req.cookies๋ฅผ ์ถ๋ ฅํด๋ณด์ธ์.
*/
const cookieId = req.cookies.cookieId;
const userInfo = {
...USER_DATA.filter((user) => user.id === cookieId)[0],
};
if (!cookieId || !userInfo.id) {
res.status(401).send("Not Authorized");
} else {
// ๋น๋ฐ๋ฒํธ๋ ๋ฏผ๊ฐํ ์ ๋ณด๋ผ์ ์ญ์ ํ์ ๋ณด๋ด์ผ ํฉ๋๋ค.
delete userInfo.password;
res.send(userInfo);
}
};
๊ทธ๋ฆฌ๊ณ
๊ฐ๋ฐ์ ๋๊ตฌ - ์ ํ๋ฆฌ์ผ์ด์ ์์
์ฟ ํคํญ์ ์ฟ ํค๋ฅผ ํ์ธํ๋ค.
๋ก๊ทธ์ธํ๋ฉด์
๋ก๊ทธ์ธ ์ํ์ ์งํ๊ธฐ ์ฒดํฌ or ์ฒดํฌX
์ผ๋
์ฒดํฌX → session ์ด ๋ค์ด์ค๊ณ
์ฒดํฌO → expires ์ค์ ํด๋๋ ๊ฒ์ด ๋ฐ ๊ฒ์ด๋ค.
ํ์ธ!
npm test → ํ ์คํธ ํต๊ณผใฑใฑ
'Code๊ฐ๋ฐ์ผ์ง' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[session] sprint-auth-session (0) | 2023.01.07 |
---|---|
Session (0) | 2023.01.07 |
Cookie (0) | 2023.01.07 |
HTTP/HTTPS (0) | 2023.01.07 |
๋คํธ์ํฌ[TCP/UDP, OSI 7] (0) | 2023.01.06 |