조회 로그인 필요 X
JWT가 필요한 부분? 좋아요 + 장바구니 담기, 내 장바구니 목록 조회, 장바구니 도서 삭제 + 결제 하기, 주문내역조회, 주문상세조회
req.header의 `Authorization` 속성값에 토근값 담아서 > payload로 사용자의 id를 읽어낸다.
JWT API
jwt, token 초기에 받은 값이 계속 유지된다. 덮어쓰기를 제대로 해주어야 한다.
res.headers에 쿠키로 토큰값이 잘 들어왔다.
토큰검증값 전달하는 방법은
req.header의 authorization 속성값에 토큰 담기
토큰값이 잘 들어온걸 볼 수 있다.
최종
//GET + "/jwt" : 토큰 발행
app.get("/jwt", function (req, res) {
var token = jwt.sign({ foo: "bar" }, process.env.PRIVATE_KEY);
res.cookie("jwt", token, { httpOnly: true });
res.send("토큰발행 완");
});
//GET + "/jwt/decoded" : 토큰 검증
app.get("/jwt/decoded", function (req, res) {
let receivedJwt = req.headers["authorization"];
console.log("우리가 req로 전달받은 jwt : " + receivedJwt);
var decoded = jwt.verify(receivedJwt, process.env.PRIVATE_KEY);
res.send(decoded);
});
프로젝트 좋아요 API 에 JWT 구현하기 + 리팩토링
임의로 받은 req.body 값의 user_id 대신! `authorization`
로그인할때 이메일도 유니크하고, id도 유니크하다. 토큰 발행 시 가져올 구분인자값으로 추가!
token의 유효시간 5분!
메소드로 추출하니, 코드의 가독성이 좋아졌다.
const addLike = (req, res) => {
let book_id = req.params.id; //book_id
let authorization = ensureAuthorization(req);
let sql =
"INSERT INTO likes (fk_likes_user_id, liked_book_id) VALUES (?, ?);";
let values = [authorization.id, book_id];
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);
});
};
const removeLike = (req, res) => {
const book_id = req.params.id;
let authorization = ensureAuthorization(req);
let sql =
"DELETE FROM likes WHERE fk_likes_user_id = ? AND liked_book_id = ?;";
let values = [authorization.id, book_id];
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);
});
};
function ensureAuthorization(req) {
let receivedJwt = req.headers["authorization"];
console.log(receivedJwt);
let decodedJwt = jwt.verify(receivedJwt, process.env.PRIVATE_KEY);
console.log(decodedJwt);
return decodedJwt;
}
장바구니 담기, 조회 JWT 구현
위와 동일한 로직!
//장바구니 담기
const addToCart = (req, res) => {
const { book_id, quantity } = req.body;
let authorization = ensureAuthorization(req);
let sql =
"INSERT INTO cartItems (book_id, quantity, user_id) VALUES (?, ?, ?);";
let values = [book_id, quantity, authorization];
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);
});
};
//장바구니 아이템 목록 조회
const getCartItems = (req, res) => {
const { selected } = req.body;
let authorization = ensureAuthorization(req);
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, 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);
});
};
//장바구니 아이템 삭제
const delCartItems = (req, res) => {
const cartItemId = req.params.id; //catrItemID
let sql = `DELETE FROM cartItems WHERE id = ?;`;
conn.query(sql, cartItemId, (err, results) => {
if (err) {
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end();
}
return res.status(StatusCodes.OK).json(results);
});
};
function ensureAuthorization(req) {
let receivedJwt = req.headers["authorization"];
console.log(receivedJwt);
let decodedJwt = jwt.verify(receivedJwt, process.env.PRIVATE_KEY);
console.log(decodedJwt);
return decodedJwt;
}
sql where절에 user_id가 존재하는지 체크하는 조건도 추가한다면, 예외처리 가능하다.
- 장바구니 아이템 목록 조회 = 내 장바구니 보기 : selected 없이 보내면 안될것같다. 분리해주어야 할듯
- jwt expired..?
- ensureAuthorization 어차피 파일마다 반볻되는것 아닌가?
JWT expired - 예외처리
토큰 발행할때 지정한 시간만큼 토큰 유효시간만 존재한다. 만료되면 500 에러가 발생!
const token = jwt.sign(
{
id: loginUser.id,
email: loginUser.email,
},
process.env.PRIVATE_KEY,
{
expiresIn: "5m",
issuer: "dah",
}
);
유효기간이 지난경우 예외처리 해야 한다 > 500 에러가 아닌, 유효기간이 지난 토큰인 경우 '로그인(인증) 세션(로그인이 유지되는 상태)이 만료되었습니다 다시 로그인 하세요'
if / else 가 아닌, 기본적으로 적용하는 코드 `try-catch`
- TokenExpiredError : 유효기간이 지난 토큰 = 만료된 토큰
- JsonWebTokenError : 문제 있는 토큰
try...catch
수많은 (개발자가 예상하지 못한) 에러(실수, 사용자 입력을 잘못한것, 디비가 응답을 잘못...)를 처리하는 문법
실수1, 실수2 비슷한 분류들끼리 묶어서 누군가.. 따로 관리를 해주면 좋겠다..
코드 A;
if(A에서 발생한 실수1){} else if(A에서 발생한 실수2){}
try{ 코드 A;} catch(err){ 에러 처리}
실수에 대한 직관적인 해석이 가능하다.
에러의 종류마다 조건을 다르게 두는게 아닌, catch 에러라는 예외를 한번에 처리할 수 있다.
try {
username;
} catch (err) {
console.log("에러");
console.log("err");
}
try 구문의 코드를 실행하다가 에러가 발생하면, try 코드를 멈추고 > 에러 err과 함께 catch로 빠져나간다.
어떠한 에러가 발생해도 if문 분기처리를 다 해주던 내용들이 > 알아서 catch에 잡힌다.
에러 객체
내장 에러 객체 만들어두었다.
`ReferenceError` > 에러 나거나 서버가 죽지 않고, 프로그램을 돌릴 수 있게 한다.
JWT 모듈에서도 제공하는 에러 객체도 있고, 직접 만들어 쓸수도 있다.
Throw 연산자
에러를 발생시키는 연산자
throw 에러 객체
ex. throw new SyntasErr();
에러객체 실체화하는 방법이 있다.
let err = new Error("대장에러객체");
let syntaxErr = new SyntaxError("구문에러발생");
let referencedErr = new ReferenceError("대입에러발생")
console.log(err.name);
console.log(err.message);
console.log(syntaxErr.name);
console.log(syntaxErr.message);
console.log(referencedErr.name);
console.log(referencedErr.message);
//console
Error
대장에러객체
SyntaxError
구문에러발생
ReferenceError
대입에러발생
if는 중간에 에러가 발생해도, 코드를 끝까지 실행하는데
try에서 에러가 발생하면 뒤의 코드를 실행하지 않고, catch의 에러코드로 바로 넘어간다.
let string = '{"num:1}';
try {
// username;
let json = JSON.parse(string);
if (!json.name) throw new SyntaxError("입력값에 이름이 없음");
else console.log(json.name);
let name = json.name;
console.log(name); //입력값이 잘몯된 에러
} catch (err) {
console.log("에러");
console.log(err.name);
console.log(err.message);
}
//console
에러
SyntaxError
Unterminated string in JSON at position 8
토큰 유효기간을 try-catch로 예외처리
function ensureAuthorization(req,res) {
try {
let receivedJwt = req.headers["authorization"];
console.log(receivedJwt);
let decodedJwt = jwt.verify(receivedJwt, process.env.PRIVATE_KEY);
console.log(decodedJwt);
return decodedJwt;
} catch (err) {
console.log(err.name);
console.log(err.message);
return res
.status(StatusCodes.UNAUTHORIZED)
.json({ message: "로그인세션이 만료되었습니다. 다시 로그인하세요" });
}
}
instanceof 키워드
두개의 res를 연속해서 보내면 발생하는 에러
위 코드에서 return에서 res 응답이 아닌 err 에러를 반환한다.
혹시 왼쪽 변수가 오른쪽 변수처럼 생겼어? 판별
const addToCart = (req, res) => {
const { book_id, quantity } = req.body;
let authorization = ensureAuthorization(req, res);
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: "잘못된 토큰입니다." });
} else {
let sql =
"INSERT INTO cartItems (book_id, quantity, user_id) VALUES (?, ?, ?);";
let values = [book_id, quantity, authorization];
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);
});
}
};
파일마다 ensureAuthorization 반복되는 함수 모듈화하기
☑️ 배운 점
발급받은 JWT를 user_id 대신 사용하기
jwt 가 유효하지 않을때 = expired 되었을때 = try-catch로 예외처리하고, throw 연산자로 에러 객체 생성해서 실체화하고, 바로 catch 예외에러코드로 넘긴다.
instanceof 키워드 : 왼쪽 변수가 오른쪽 변수처럼 생겼는지 판별하는 키워드이다.
try-catch를 가볍게 알고 있었는데, 좀더 심화되어서 알게되었다. 굳굳