내일배움캠프 스파르타 코딩클럽
강의를 다루기 전 오늘 코드카타에서 푼 문제를 잠시 다루고자 한다.
문제 :
자연수 n이 매개변수로 주어집니다. n을 3진법 상에서 앞뒤로 뒤집은 후, 이를 다시 10진법으로 표현한 수를 return 하도록 solution 함수를 완성해주세요.
입출력 예
n (10진법) |
n (3진법) |
앞뒤 반전 (3진법) |
10진법으로 표현 |
45 |
1200 |
0021 |
7 |
내가 해결한 답
function solution(n) {
var answer = 0;
let ternary = n.toString(3);
let reverseTernary = ternary.split('').reverse().join('');
answer += parseInt(reverseTernary, 3);
return answer;
}
1. toString
보통 toString 메서드는 문자열로 변환할때 많이 사용하곤 한다.
그러나 toString 파라미터에 매개변수를 넣어 줄 경우 위와같이 진법으로 변환하여 문자열로 반환해준다.
2. reverse
문자열 앞뒤를 반전시켜준다.
대신 위와같은 경우 문자열이 하나이기 때문에 그 문자열을 split을 통하여 나눠준 뒤 반전시키고 다시 join으로 붙여준다.
3. parseInt 이 또한 정수로 변환해주는 메서드인데
파라미터에 매개변수를 넣어준다면 위와 같이 10진법으로 변환이 가능하다.
parseInt의 첫번째 파라미터에는 문자열이 들어가며
두번째는 문자열을 어떠한 진법으로 인식하고 10진법으로 변환 할 것인지를 나타낸다.
< 회고 >
진법과 진수의 차이
메서드를 사용하지 않고 진법과 진수를 구한다면?
이어서 숙련 강의 게시판 프로젝트를 이어 가보도록 하겠다.
[ 엑세스, 리프레시 토큰발급 API ]
Access Token:
토큰을 생성할때 사용한 비밀키(Secret key)로 인증을 처리하게 되고,
이 방식은 복잡한 설계나 여러 분기 처리 없이 코드를 구현할 수 있다는 장점을 가지고 있다.
Stateless(무상태) 즉, Node.js 서버가 재시작 되더라도 동일하게 작동을 하게 되고
이는 jwt를 이용해 사용자의 인증 여부는 확인할 수 있지만, 처음 토큰을 발급한 사용자가 정말
그 사용자인지는 확인할 수 없다.
Access Token은 그 자체로도 사용자 인증에 필요한 모든 정보를 가지고 있고
토큰을 가지고 있는 시간이 늘어날 수록 탈취되었을 때 피해 규모는 더욱 커지게 된다.
그래서 토큰이 탈취되었다 하더라도 서버에서는 해당 토큰이 탈취된토큰인지 알 수 없으며,
강제로 토큰을 만료시킬 수도 없다.
따라서 서버는 언제나 토큰이 탈취될 수 있다는 가정 하에 피해를 최소화할 수 있는 방향으로 개발을 진행해야 한다.
이러한 문제점을 해결 하기 위하여 다음 Refresh Token 을 알아보겠다.
Refresh Token:
사용자의 모든 인증정보를 담고 있는 Access Token과 달리,
특정 사용자가 Access Token을 발급받기 위한 목적으로만 사용
사용자의 인증 정보를 검증하는데 사용되며, 이를 서버에서 관리한다.
일상생활에서 흔히 사용하는 OTP처럼, 사용자의 인증 정보는 짧은 시간동안만 사용되야 한다.
그러므로 짧은 기간 동안만 사용 가능하도록 제한하여 피해를 최소화할 수 있게 될 것이다.
동작법을 그림으로 표현하자면 다음과 같다. 출처: 내일배움캠프 스파르타 코딩클럽 숙련주차 강의
< README.md >
# history
1. yarn init -y
2. yarn add express jsonwebtoken cookie-parser
< 디렉터리 >
< package.json >
{
"name": "access-refresh",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"type": "module",
"dependencies": {
"cookie-parser": "^1.4.6",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2"
}
}
< app.js >
// app.js
import express from 'express';
import jwt from 'jsonwebtoken';
import cookieParser from 'cookie-parser';
const app = express();
const PORT = 3019;
// 비밀 키는 외부에 노출되면 안되겠죠? 그렇기 때문에, .env 파일을 이용해 비밀 키를 관리해야합니다.
const ACCESS_TOKEN_SECRET_KEY = `HangHae99`; // Access Token의 비밀 키를 정의합니다.
const REFRESH_TOKEN_SECRET_KEY = `Sparta`; // Refresh Token의 비밀 키를 정의합니다.
app.use(express.json());
app.use(cookieParser());
app.get('/', (req, res) => {
return res.status(200).send('Hello Token!');
});
const tokenStorages = {} // 리프레시 토큰을 관리할 객체
// 엑세스, 리프레시 토큰발급 API
app.post('/tokens', async (req, res, next) => {
// ID 전달
const { id } = req.body;
// 엑세스 토큰과 리프레스 토큰을 발급
const accessToken = jwt.sign({ id: id }, ACCESS_TOKEN_SECRET_KEY, { expiresIn: '10s' });
const refreshToken = jwt.sign({ id: id}, REFRESH_TOKEN_SECRET_KEY, { expiresIn: '7d' });
tokenStorages[refreshToken] = {
id: id,
ip: req.ip,
userAgent: req.headers['user-agent']
}
// 클라이언트에게 쿠키(토큰)를 할당
res.cookie('accessToken', accessToken);
res.cookie('refreshToken', refreshToken);
return res.status(200).json({ message: 'Token이 정상적으로 발급되었습니다.' });
});
app.listen(PORT, () => {
console.log(PORT, '포트로 서버가 열렸어요!');
});
위와 같이 작성을하고 insomnia 로 작동을 시켜본 결과는 아래와 같다.
[ Access Token 검증 API ]
< app.js >
// Access Token 검증 API
app.get('/tokens/validate', async (req, res) => {
const { accessToken } = req.cookies;
// 엑세스 토큰이 존재하는지 확인한다.
if (!accessToken) {
return res.status(400).json({ errorMessage: 'Access Token 이 존재하지 않습니다.'})
}
const payload = validateToken(accessToken, ACCESS_TOKEN_SECRET_KEY)
if (!payload) {
return res.status(401).json({ errorMessage: 'Access Token이 정상적이지 않습니다.' })
}
const { id } = payload;
return res.status(200).json({ message: `${id}의 payload를 가진 Token이 정상적으로 인증 되었습니다.`})
})
// Token 을 검증하고, Payload를 조회하기 위한 함수
function validateToken(token, secretkey) {
try {
return jwt.verify(token, secretkey);
} catch (err) {
return null;
}
}
Access Token을 발급한뒤 정상적으로 검증 완료
Access Token 을 발급받은뒤 10초가 지나 만료된 후 검증을 진행한 경우
[ Access Token 재발급 API ]
< app.js >
function createAccessToken(id) {
return jwt.sign({ id }, ACCESS_TOKEN_SECRET_KEY, { expiresIn: '10s' });
}
// Refresh Token을 이용해서, AccessToken을 재발급하는 API
app.post('/tokens/refresh', async (req, res) => {
const { refreshToken } = req.cookies;
if(!refreshToken) {
return res.status(400).json({ errorMessage: 'Refresh Token이 존재하지 않습니다.'});
}
const payload = validateToken(refreshToken, REFRESH_TOKEN_SECRET_KEY);
if(!payload) {
return res.status(401).json({ errorMessage: 'Refresh Token이 정상적이지 않습니다.'});
}
const userInfo = tokenStorages[refreshToken];
if(!userInfo) {
return res.status(419).json({ errorMessage: 'Refresh Token의 정보가 서버에 존재하지 않습니다.'});
}
const newAccessToken = createAccessToken(userInfo.id);
res.cookie('accessToken', newAccessToken)
return res.status(200).json({ message: 'Access Token을 정상적으로 새롭게 발급했습니다.'})
})
이렇게 해서 엑세스, 리프레시 토큰에 대해서 알아 보았다.
다음 시간은 미들웨어에 대해 좀 더 알아보겠다.