๐๏ธ JWT ์ฌ์ฉํด๋ณด๊ธฐ
๋ก๊ทธ์ธํ ๋ ๋ฐํํด์ฃผ๋ ํ ํฐ์ผ๋ก ์์ฒญ์ ๋ณด๋ธ ์ฌ์ฉ์๊ฐ ๋๊ตฌ์ธ์ง ํ๋ณํด์ฃผ๋ ค๊ณ ํ๋ค.
๋จผ์ ํ ํฐ์ ์ด๋ป๊ฒ ํ์ฉํ๋ ์ง ์ฐ์ต๋ถํฐ ํด๋ณด์.
var jwt = require('jsonwebtoken');
const express = require('express');
const app = express();
const dotenv = require('dotenv');
dotenv.config();
app.listen(process.env.PORT);
app.get('/', function (req, res) {
var token = jwt.sign({ foo : 'bar'}, process.env.PRIVATE_KEY);
res.cookie("jwt", token, {
httpOnly : true
});
res.send("ํ ํฐ ๋ฐํ ์๋ฃ");
});
๋จผ์ ํ ํฐ ๋ฐํ์ jsonwebtoken ๋ชจ๋์ ํ์ฉ ํด ํ์ด๋ก๋์ ๋๋ง์ ์ํธํค๋ก ๋ฐํํด์ฃผ๊ณ ์๋ค.
๊ทธ๋ ๊ฒ ๋ฐํ๋ ํ ํฐ์ ์ฟ ํค์ ๋ด์์ ๋ฐํํด์ค๋ค.
ํฌ์คํธ๋งจ์ผ๋ก ํ์ธํด๋ณด๋ Response Headers์ ์ ๋ด๊ฒจ ์๋ค.
ํ ๋ฒ ๋ ๋ฐํํด๋ณธ ๋ค ํ์ธํ๋๋
Request Headers ์ฟ ํค์๋ ํ ํฐ์ด ๋ด๊ฒจ์๋ค. ๊ทธ๊ฒ๋ ์ด์ ์์ฒญ์์ ๋ฐ์ ํ ํฐ๊ฐ์ด ๋ค์ด๊ฐ์๋ค.
์ฌ์ค ์ฟ ํค๋ ์๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ ๊ธฐ์กด์ ๊ฐ์ ๊ณ์ ๊ฐ์ง๊ณ ๋ค๋๋ค.
๊ฐ์ ์ด๋ฆ์ ์๋ก์ด ๊ฐ์ด ๋ค์ด์ค๋ฉด ๋ฎ์ด์ฐ๊ธฐ๋ฅผ ํ๋ ๋ฐฉ์์ด๋ผ ๊ทธ๋ ์ง ์๋๋ค๋ฉด ๊ทธ ๊ฐ์ ๊ณ์ ๊ฐ์ง๊ณ ์๋ ๊ฒ์ด๋ค.
๊ทธ๋ฌ๋ค๋ณด๋ ์๋ก์ด ๊ฐ์ด ๋ค์ด์ฌ ๋๋ง๋ค ์ฟ ํค์ ๊ธธ์ด๋ ์ ์ ๊ธธ์ด์ง๊ฑฐ๊ณ
๋ฎ์ด์ฐ๊ธฐ๊ฐ ์ ๋๋ก ์๋๋ ๊ฒฝ์ฐ ์์ ๊ฐ์ ๊ณ์ ๊ฐ์ง๊ณ ์๋๋ค.
๊ทธ๋ ๊ฒ ๋๋ฌธ์ ์ฟ ํค ์ญ์ ๊ฐ ํ์ํ๋ค.
์ด๋ฒ์๋ ์ฟ ํค๋ก ๋ค์ด์จ ํ ํฐ์ ๋ฐ์์ ๋ณตํธํ๋ฅผ ํด๋ณด์.
์์ฒญํ ๋ ํ ํฐ์ ์ฌ์ฉํ๋ ค๋ฉด Headers์ ๋ด์์ฃผ๋ฉด ๋๋ค.
app.get('/jwt/decoded', function (req, res) {
let receivedJWT = req.headers["authorization"]
var decoded = jwt.verify(receivedJWT, process.env.PRIVATE_KEY);
res.send(decoded);
});
headers ๊ฐ์ ๋ฐ์๋ค ๋ณตํธํํ๋ ์ฝ๋์ ๋ฃ์ด์ฃผ๊ธฐ๋ง ํ๋ฉด ๋๋ค.
๊ทธ๋ผ ๋ณตํธํ๋ ๊ฐ์ด ์๋ต์ ์ ๋จ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๐ฉท ์ข์์ API์ JWT ๊ตฌํ
์์ ์ข์์ API ๊ตฌํ ์ req.body์ userId๋ฅผ ๋ฐ์ ์ฌ์ฉํ๋ค.
์ด์ jwt๋ฅผ ํ์ฉํด id๊ฐ์ ๋ฐ์์ค๋๋ก ์์ ํ๋ ค ํ๋ค.
const token = jwt.sign({
id : loginUser.id,
email : loginUser.email
}, process.env.PRIVATE_KEY, {
expiresIn : '5m',
issuer : 'mingyeong'
});
๋จผ์ id๊ฐ์ด ํ์ํ๋ ํ ํฐ ๋ฐํํ ๋ ์ถ๊ฐํด์ฃผ์๋ค.
ํ ํฐ์ ๋ณตํธํํด๋ณด๋ฉด ์ด๋ ๊ฒ id์ email์ด ํจ๊ป ๋์จ๋ค.
์ด์ ์ด id๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
let sql = `INSERT INTO likes (user_id, liked_book_id) VALUES (?, ?)`;
let values = [decodedJWT.id, book_id];
sql๋ฌธ์ values์ ๋ณตํธํ๋ jwt์ id๋ก ๋ฃ์ด์ฃผ๋ฉด ๋ !
์ด์ body์ user_id๋ฅผ ๋ฃ์ด์ฃผ์ง ์์๋ ํ ํฐ์ ํ์ฉ ํด id๋ฅผ ์ฐพ์ ์์ฒญ์ ๋ณด๋ด์ค ์ ์๊ฒ ๋์๋ค.
๐ user_id ๊บผ๋ด๊ธฐ ๋ชจ๋ํ
user_id๊ฐ ํ์ํ API ์์ฒญ์ด ๋ง์ผ๋ ๋ชจ๋ํ๋ฅผ ํด์ค ํ์๊ฐ ์๋ค.
(๋น์ฅ ์ข์์ ์ทจ์ํ ๋๋ ๋์ผํ๊ฒ ํ์ํ๋ค.)
function ensureAuthorization(req) {
let receivedJWT = req.headers["authorization"];
console.log("receivedJWT : ", receivedJWT);
let decodedJWT = jwt.verify(receivedJWT, process.env.PRIVATE_KEY);
console.log("decodedJWT : ", decodedJWT);
return decodedJWT;
}
Headers์์ jwt๋ฅผ ๊บผ๋ด๊ณ ๋ณตํธํํ๋ ์ฝ๋๋ฅผ ํจ์๋ก ๋นผ๋ด์ฃผ์๋ค.
let authorization = ensureAuthorization(req);
์ด์ ํ์ํ ๊ณณ์์ ํจ์๋ฅผ ํธ์ถํ๋ฉด user_id๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์ด๋ ๊ฒ ํจ์๋ก ํ์ฉํ๋ฉด ํจ์๋ช ์ ํตํด ์ฝ๋ ํด์ํ๋ ๋ฐ ๋์์ ์ค๋ค.
๐ก ํจ์๋ช ์ ๋์ฌ๊ฐ ์์ผ๋ก ์ค๋๋ก ํ์ฌ ๋ญํ๋ ํจ์์ธ์ง ๋ช ํํ๊ฒ ๋ํ๋ด ์ฃผ๋ ๊ฒ์ด ์ข๋ค.
๋ํ ํจ์๋ฅผ ํธ์ถํ๋ ์์น๊ฐ ์ ์ธํ๋ ์์น๋ณด๋ค ์์ ์์ด์ผ ํ๋ฒ๋ง ์ฝ์ด๋ ์ดํดํ๊ธฐ ์ข๋ค.
๐ฉ jwt expired
๊ฐ์ ํ ํฐ์ผ๋ก ์์ฒญํ์ง๋ง ํ ํฐ์ ์ ํจ๊ธฐ๊ฐ์ด ์ง๋๋ฉด expired ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
500์๋ฌ๋ ์๋ฒ์ธก์ด ์๋ชป๋์๋ค๋ ์๋ฌ๋ก ์น๋ช ์ ์ธ ์๋ฌ๋ค
ํ ํฐ์ ์ ํจ๊ธฐ๊ฐ ๋ง๋ฃ๋ 500์๋ฌ๋ฅผ ๋ผ ๊ฒ ์๋๊ณ ์์ธ ์ฒ๋ฆฌ๋ฅผ ํด์ค์ผ ํ๋ค.
์ ํจ๊ธฐ๊ฐ์ด ์ง๋ ํ ํฐ์ผ๋ก ์์ฒญ์ด ๋ค์ด์๋ค๋ฉด ๐๐ป ' ๋ก๊ทธ์ธ(์ธ์ฆ) ์ธ์ (์ ์ง๋๋ ์ํ)์ด ๋ง๋ฃ๋์์ต๋๋ค. ๋ค์ ๋ก๊ทธ์ธ ํ์ธ์.'
โ๏ธ ์์ธ ์ฒ๋ฆฌ
node์์ ์ค๋นํด ๋ jwt ์๋ฌ 3๊ฐ์ง
- JsonWebTokenError : ๋ฌธ์ ์๋ ํ ํฐ
- NotBeforeError : jwt๊ฐ ์๋ ๊ฒฝ์ฐ
- TokenExpiredError : ์ ํจ๊ธฐ๊ฐ ํ ํฐ = ๋ง๋ฃ๋ ํ ํฐ
try catch ๊ตฌ๋ฌธ
: ์๋ง์ (๊ฐ๋ฐ์๊ฐ ์์ํ์ง ๋ชปํ) ์๋ฌ(์ค์, ์ฌ์ฉ์๊ฐ ์๋ชป ์ ๋ ฅ, DB๊ฐ ์๋ชป ์๋ต ๋ฑ ..)๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฌธ๋ฒ
// A์ฝ๋ ์คํ
if (A์์ ๋ฐ์ํ ์ค์1) {
} else if (A์์ ๋ฐ์ํ ์ค์2) {
} …
if๋ฌธ์ ๊ฒฝ์ฐ์ ์๊ฐ ์์์ด ๋์ด๋ ์ ์๋ค.
try {
// A์ฝ๋ ์คํ
} catch (err) {
// ์๋ฌ ์ฒ๋ฆฌ
}
try catch๋ ์ฌ๋ฌ ๊ฐ ๋ง๋ค์ง ์์๋ ์ด๋ค ์ค์๊ฐ ๋ฐ์ํ๋ ์ง ์ ์ ์๋ค.
์๋ฌ๋ ๋ณดํต ์คํํด๋ด์ผ ์ ์ ์๋ค.
์ฆ, if๋ฌธ์ผ๋ก ๋ฏธ๋ฆฌ ์ฒ๋ฆฌํด์ค ์ ์๋ค.
try catch๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ค ์๋ฌ์ฌ๋ ์ก์์ค๋ค.
try ๊ตฌ๋ฌธ์ ์ฝ๋๋ฅผ ์คํํ๋ค๊ฐ ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด try๊ตฌ๋ฌธ์ ๋ฉ์ถ๊ณ catch๋ก err์ ํจ๊ป ๋น ์ ธ๋๊ฐ๋ค.
try ๊ตฌ๋ฌธ์์ ์ด๋ค ์๋ฌ๊ฐ ๋ฐ์ํด๋, ์ฐ๋ฆฌ๊ฐ ๋ค if๋ฌธ ๋ถ๊ธฐ ์ฒ๋ฆฌ ํด์ฃผ๋ ๋ด์ฉ๋ค์ด ์์์ catch์ ์กํ๋ค.
ex. SyntaxError, TypeError …
๐ฅ ์๋ฌ ๊ฐ์ฒด
์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ๊ณ ๋ ๊ฐ๋ฐ์๋ถ๋ค์ ๋ ธ๊ณ ๋ฅผ ๋ณด๊ณ "๋ด์ฅ" ์๋ฌ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด๋์๋ค.
์์ ์ธ๊ธํ jwt ๋ชจ๋์์ ์ ๊ณตํ๋ ์๋ฌ ๊ฐ์ฒด๋ ์๊ณ ์ง์ ๋ง๋ค์ด ์ฌ์ฉํ ์๋ ์๋ค.
์ด์ฒ๋ผ ์๋ฌ๋ ๊ฐ์ฒด์ด๊ธฐ์ ์ด๋ฆ๊ณผ ๋ด์ฉ์ ๋ฐ๋ก ๋ถ๋ฌ์ค ์ ์๋ค.
โพ Throw ์ฐ์ฐ์
: ์๋ฌ๋ฅผ ๋ฐ์์ํค๋ ์ฐ์ฐ์
throw ์๋ฌ ๊ฐ์ฒด;
throw new SyntaxError(๋ฉ์ธ์ง);
throw ๋ค์ ๋์ค๋ ์๋ฌ๋ฅผ ๋ฐ์์ํค๋ ๊ตฌ๋ฌธ
์๋ฌ ์ด๋ฆ์ ์ง์ ํด์ค ์ ์์ง๋ง ๋งค๊ฐ๋ณ์๋ก ๋ฉ์์ง๋ฅผ ์ ๋ฌํด์ค ์ ์๋ค.
๊ทธ๋ผ ์๋ฌ ๋ฐ์์ ์ธ์ ์์ผ์ฃผ๋ ๊ฑธ๊น?
JSON.parse๊ฐ ๋ฌธ์ ์์ด ๋์ด๊ฐ๊ธด ํ์ง๋ง json.name์ ์๋ ๊ฐ์ธ๋ฐ ์๋ฌ๊ฐ ์๋ undefined๋ฅผ ๋ฐํํด์ฃผ๊ณ ์๋ค.
์๋ฌ๊ฐ ๋ฐ์ํ์ง ์์๊ธฐ ๋๋ฌธ์ try catch๋ฅผ ์ฌ์ฉํด๋ ๋์ผํ ๊ฒฐ๊ณผ๊ฐ ๋ํ๋๋ค.
js์ ์ฅ์์ ์๋ฌ๊ฐ ์๋ ์ง ๋ชฐ๋ผ๋ ์ฐ๋ฆฌ ์ ์ฅ์์ ์ด๊ฑด ์ ๋ ฅ๊ฐ์ด ์๋ชป๋ ์๋ฌ๋ค !
์ด๋ด ๋ ์ฌ์ฉํ๋ ๊ฒ์ด Throw ์ธ ๊ฒ์ด๋ค.
if๋ฌธ์ผ๋ก๋ ์์ธ ์ฒ๋ฆฌ๋ฅผ ํด์ค ์ ์์ง๋ง ์ดํ๋ก ์ค๋ ์ฝ๋๋ค๋ ๊ณ์ ์คํ์ด ๋๋ค.
throw๋ก ์๋ฌ๋ฅผ ๋์ ธ์ฃผ๋ฉด ๋ฐ๋ก catch๋ก ๋์ด๊ฐ ์ดํ ์ฝ๋๋ ์คํ๋์ง ์๋๋ค.
๐งจ TokenExpiredError & JsonWebTokenError
function ensureAuthorization(req) {
try {
let receivedJWT = req.headers["authorization"];
console.log("receivedJWT : ", receivedJWT);
let decodedJWT = jwt.verify(receivedJWT, process.env.PRIVATE_KEY);
console.log("decodedJWT : ", decodedJWT);
return decodedJWT;
} catch (err) {
console.log(err.name);
console.log(err.message);
}
};
์ด์ ensureAuthorization์์ ์๋ฌ ๋ฐ์ ์ ์๋ฌ๋ฅผ ๋์ ธ์ค๋ค.
ํ์ง๋ง ensureAuthorization์ ํธ์ถํ๋ ๊ณณ์์ ์์ธ ์ฒ๋ฆฌ๊ฐ ์๋ผ ์๋ ์ฝ๋๊น์ง ์คํ๋์๋ค.
ensureAuthorization์ด ์๋ฌ๊ฐ ๋ฌ๊ธฐ์ authorization.id๊ฐ ์์ด์ ๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
function ensureAuthorization(req, res) {
try {
let receivedJWT = req.headers["authorization"];
console.log("receivedJWT : ", receivedJWT);
let decodedJWT = jwt.verify(receivedJWT, process.env.PRIVATE_KEY);
console.log("decodedJWT : ", decodedJWT);
return decodedJWT;
} catch (err) {
console.log(err.name);
console.log(err.message);
return res.status(StatusCodes.UNAUTHORIZED).json({
"message" : "๋ก๊ทธ์ธ ์ธ์
์ด ๋ง๋ฃ๋์์ต๋๋ค. ๋ค์ ๋ก๊ทธ์ธ ํ์ธ์."
});
}
};
๊ทธ๋์ ensureAuthorization ์์ ์๋ฌ๊ฐ ๋ ๊ฒฝ์ฐ ์๋ฌ๋ก ์๋ตํด์ฃผ๋๋ก ์์ ํ๋ค.
๊ทธ๋ฌ๋๋ ์ด๋ฒ์ ERR_HTTP_HEADERS_SENT ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
์ด ์๋ฌ๋ res๋ฅผ ๋๋ฒ๋ณด๋ด์ ๋ฐ์ํ ์๋ฌ๋ค.
ensureAuthorization ์์ ์๋ฌ๊ฐ ๋์ res๋ฅผ ๋ณด๋๋๋ฐ
getCartItems์์ ์ฟผ๋ฆฌ์์ ๋ ์๋ฌ๊ฐ ๋์ res๋ฅผ ๋ ๋ณด๋๊ธฐ ๋๋ฌธ์ด๋ค.
ensureAuthorization ์์ res๋ฅผ ๋ณด๋ด์ง ๋ง๊ณ err๋ง ๋ฐํํด์ฃผ๊ณ
getCartItems์์ res๋ฅผ ๋ณด๋ด์ฃผ๋ ๊ฑธ๋ก ์์ ํด์ผ๊ฒ ๋ค.
const getCartItems = (req, res) => {
const {selected} = req.body;
let authorization = ensureAuthorization(req, res);
if(authorization instanceof jwt.TokenExpiredError) {
return res.status(StatusCodes.UNAUTHORIZED).json({
"message" : "๋ก๊ทธ์ธ ์ธ์
์ด ๋ง๋ฃ๋์์ต๋๋ค. ๋ค์ ๋ก๊ทธ์ธ ํ์ธ์."
});
} else {
let sql = `SELECT cartItems.id, book_id, title, summary, quantity, price
FROM cartItems LEFT JOIN books
ON cartItems.book_id = books.id
WHERE user_id = ? AND cartItems.id IN (?)`;
let values = [authorization.id, selected];
conn.query(sql, values,
(err, results) => {
if(err) {
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end();
}
return res.status(StatusCodes.OK).json(results);
}
);
}
};
instanceof : ์ผ์ชฝ ๋ณ์๊ฐ ์ค๋ฅธ์ชฝ ๋ณ์์ฒ๋ผ ์๊ฒผ๋ ์ง ํ์ธํ๋ ๊ตฌ๋ฌธ
์ด๋ฒ์ ์๋ชป๋ ํ ํฐ์ด ๋ค์ด๊ฐ ๊ฒฝ์ฐ, JsonWebTokenError๋ ์ฒ๋ฆฌํด๋ณด์.
if(authorization instanceof jwt.TokenExpiredError) {
return res.status(StatusCodes.UNAUTHORIZED).json({
"message" : "๋ก๊ทธ์ธ ์ธ์
์ด ๋ง๋ฃ๋์์ต๋๋ค. ๋ค์ ๋ก๊ทธ์ธ ํ์ธ์."
});
} else if (authorization instanceof jwt.JsonWebTokenError) {
return res.status(StatusCodes.BAD_REQUEST).json({
"message" : "์๋ชป๋ ํ ํฐ์
๋๋ค."
});
}
TokenExpiredError์ ๋์ผํ ๋ฐฉ๋ฒ์ผ๋ก ์ฒ๋ฆฌํด์ฃผ์๋ค.
๊ทธ๋ฐ๋ฐ ์ด๊ฑธ ๋งค ์ฝ๋๋ง๋ค ์ฐ๊ธฐ์ ๋๋ฌด ๋ฐ๋ณต๋๋ค.
์ธ๋ถ ํ์ผ์ ์์ฑ ํ ๋ชจ๋์ฒ๋ผ ๋ถ๋ฌ์ ์ฌ์ฉํ๋๋ก ํด๋ด์ผํ ๊ฒ ๊ฐ๋ค.
'๐๏ธ DevCourse > Backend' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[TIL] Week 10 bookShop ๊ณ ๋ํ ๋ฐ ๋๋ค ๋ฐ์ดํฐ API (3) | 2025.03.24 |
---|---|
[TIL] Week 9 ์ฃผ๋ฌธ API ์ต์ข ..! (2) | 2025.03.20 |
[TIL] Week 9 ๋น๋๊ธฐ ์ฒ๋ฆฌ (1) | 2025.03.19 |
[TIL] Week 9 ์ฃผ๋ฌธ API (3) | 2025.03.18 |
[TIL] Week 9 ์ฅ๋ฐ๊ตฌ๋ API (2) | 2025.03.17 |