jwt api
좋아요 api, 장바구니 api, 주문 api에 jwt가 필요하다.
쿠키에 jwt를 보내놓으면 http에서 기억해뒀다가 다음 요청에서 그 값을 쿠키에 자동으로실어서 보낸다.
이 값을 그대로 쿠키에 넣는것이 아닌 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 token = req.headers["authorization"];
console.log("우리가 req로 전달받은 jwt : ", token);
var decoded = jwt.verify(token, process.env.PRIVATE_KEY);
res.send(decoded);
})
headers 를 통해 헤더의 값을 받아올 수 있다. 이를 복호화 하여 응답에 돌려주면
이와같이 제대로 응답이 오는것을 알 수 있다.
좋아요 jwt
좋아요 api에 jwt를 적용할 수 있다.
const addLike = (req, res) => {
const {liked_book_id} = req.params;
let authorization = ensureAuthorization(req);
let sql = `INSERT INTO likes (user_id, liked_book_id)
VALUES (?, ?)`;
let values = [authorization.id, liked_book_id]
conn.query(sql, values, (err, results) => {
if(err) {
console.log(err);
return res.status(StatusCodes.INTERNAL_SERVER_ERROR);
}
return res.status(StatusCodes.OK).json(results);
})
};
function ensureAuthorization(req) {
let receivedJwt = req.headers["authorization"];
console.log("received jwt : ", receivedJwt);
let decodedJwt = jwt.verify(receivedJwt, process.env.PRIVATE_KEY);
console.log(decodedJwt);
return decodedJwt;
};
header의 authorization 에서 jwt 토큰을 받아오고 이를 복호화 한다.
거기서 얻어진 객체를 통해 user_id 값을 얻어올 수 있다.
jwt expired
jwt 에는 유효기간이 있다. 이 유효기간이 지난 토큰을 보내게 되면 오류가 나게 된다.
포스트맨에서 500코드가 찍히는 것을 알 수 있다. 이는 따로 예외처리가 필요하다.
500 에러를 띄울게 아니라 '로그인(인증) 세션이 만료되었습니다. 다시 로그인 하세요.' 라는 메시지를 띄워주어야 한다.
jwt 예외처리
jwt의 예외처리를 위해 try catch 문과 Error 객체를 사용한다.
- TokenExpiredError: 유효기간이 지난 토큰 = 만료된 토큰
- JsonWebTokenError: 토큰 자체에 문제가 있음
try catch
수많은 (개발자가 예상하지 못한) 에러(실수, 사용자 입력잘못, 데이터베이스 문제)를 처리하는 문법
try {
//오류가 생길 수 있는 코드
} catch(err) {
// 에러 처리
}
다음은 실제 사용예시이다.
let str = '{ "num1" : 1 ';
try{
//username;
let json = JSON.parse(str);
console.log(json);
} catch(err) {
console.log(err);
}
// SyntaxError: Expected ',' or '}' after property value in JSON at position 13
try 구문의 코드를 실행하다 에러가 발생하면, try 코드를 멈추고 catch로 err와 함께 바로 빠져나간다.
어떤 에러가 나든 catch(err)에서 알아서 잡아준다. ex) SyntaxError, TypeError
에러 객체
자바스크립트가 자주 생기는 에러를 분류한 "내장 에러객체"를 만들어두었다.
JWT 모듈에서 제공하는 에러 객체도 있다. 또 우리가 직접 만들어서 쓸 수도 있다.
let str = '{ "num1" : 1 ';
try{
//username;
let json = JSON.parse(str);
console.log(json);
} catch(err) {
console.log(err.name);
console.log(err.message);
}
// SyntaxError
// Expected ',' or '}' after property value in JSON at position 13
throw
개발자가 에러를 발생시키는 연산자
throw new 에러 객체(메시지)
이 throw는 왜 필요할까? 다음 예시를 보면 알 수 있다.
let str = '{ "num1" : 1 }';
try{
//username;
let json = JSON.parse(str);
if(!json.name) {
console.log("입력 값에 이름이 없습니다.");
} else{
console.log(json.name); // js 입장에서는 에러가 아니지만, 우리 입장에서는 에러!
}
let name = json.name;
console.log(name);
} catch(err) {
console.log(err.name);
console.log(err.message);
}
json에서 없는 필드명인 name을 가져온다고 가정해보자. json.name을 로그를 찍어도 js 입장에서는 이를 에러라 인식하지 않고 그냥 undefined를 반환해준다. 이것을 처리하려면 if 문으로 값의 유무를 판단해주면 된다고 생각할 수도 있다. 하지만 조건문으로만 처리하면 문제가 하나 있다. 오류가 발생해도 try문의 모든 코드를 계속 실행한다는 것이다. 왜냐하면 js 입장에선 에러라 인식하지 못했기 때문이다. 이를 위해 필요한 연산자가 throw 이다.
let str = '{ "num1" : 1 }';
try{
//username;
let json = JSON.parse(str);
if(!json.name) {
throw new SyntaxError("입력 값에 이름이 없습니다.");
} else{
console.log(json.name); // js 입장에서는 에러가 아니지만, 우리 입장에서는 에러!
}
let name = json.name;
console.log(name);
} catch(err) {
console.log(err.name);
console.log(err.message);
}
이와 같이 에러를 직접 발생시켜주면 아래 코드를 실행하지 않고 바로 catch 문으로 넘어가는 것을 알 수 있다.
ERR_HTTP_HEADERS_SENT
function ensureAuthorization(req, res) {
try{
let receivedJwt = req.headers["authorization"];
console.log("received jwt : ", 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" : "로그인 세션이 만료되었습니다. 다시 로그인 하세요."
});
}
};
유효기간 지난 토큰 처리를 위해 ensureAuthorization 함수에 try catch 문을 적용해보았다. 이렇게 하면 에러처리가 잘 될 것같지만 한가지 더 문제가 생긴다. 바로 핸들러 함수에서 res를 한번 더 보내기때문에 에러가 또 발생한다. 이때 발생하는 에러가 ERR_HTTP_HEADERS_SENT 에러이다.
instanceof
function ensureAuthorization(req, res) {
try{
let receivedJwt = req.headers["authorization"];
console.log("received jwt : ", 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 err;
}
};
let authorization = ensureAuthorization(req, res);
if(authorization instanceof jwt.TokenExpiredError) {
return res.status(StatusCodes.UNAUTHORIZED).json({
"message" : "로그인 세션이 만료되었습니다. 다시 로그인 하세요."
});
}
ensureAuthorization 에서 err 객체를 반환해주고 핸들러에서는 조건문으로 반환된 값이 TokenExpiredError의 객체인지 여부를 확인한다. 이렇게 하면 더 이상 에러가 생기지 않는다.
JsonWebTokenError
JsonWebTokenError은 토큰 그 자체에 문제가 있을경우 생기는 에러이다. 이 에러도 마찬가지의 방식으로 처리해줄 수 있다.
else if (authorization instanceof jwt.JsonWebTokenError) {
return res.status(StatusCodes.BAD_REQUEST).json({
"message" : "잘못된 토큰입니다."
});
}
이번에는 상태코드로 UNAUTHROIZED 가 아닌 BAD_REQUEST를 보내준다.
후기
jwt를 본격적으로 적용하였고 jwt로 생기는 에러를 try catch문으로 처리하는 방법에 대해서도 배웠다.
키워드: 프로그래머스 데브코스, 국비지원교육, 코딩부트캠프
'프로그래머스 풀스택 데브코스 > 데브코스 TIL' 카테고리의 다른 글
웹 풀사이클 데브코스 TIL 42일차 (0) | 2024.01.16 |
---|---|
웹 풀사이클 데브코스 TIL 41일차 (0) | 2024.01.15 |
웹 풀사이클 데브코스 TIL 39일차 (0) | 2024.01.11 |
웹 풀사이클 데브코스 TIL 38일차 (0) | 2024.01.10 |
웹 풀사이클 데브코스 TIL 37일차 (0) | 2024.01.10 |