https://github.com/vvo/iron-session
iron-session은 암호화된 쿠키를 이용하는 Node.js stateless(무상태) session utility 입니다. JWT와 비슷하지만, 전송되는 토큰 정보(payload)를 암호화 한다는 점에서 차이가 있습니다.
payload 생성
payload를 암호화한다.
쿠키로 전송 (쿠키는 client에서 server로 요청을 보낼때 마다 같이 전송됨)
쿠키를 받아 복호화하고 payload 조회
user에게 쿠키를 주고 유저가 요청을 보냈을 때 누군지 알 수 있도록 할 것인데 iron-session을 이용 할 것이다.
{
id:1
}
-----> encrypt ------> asasdfasdfasdf
asasdfasdfasd ------> decrypt -----> { id:1 }
우리가 user 정보를 가진 객체를 만들면(token의 유무 판단) 이 payload를 암호화하게 된다.
그리고 이 암호화 된 정보를 user에게 쿠키로 전송하게 된다. 쿠키는 user가 백엔드로 요청을 보낼 때 마다 요청과 함께 전송 될 것이다. 우리가 쿠키를 받게 되면 쿠키를 복호화(암호문을 평문으로 변환하는 과정)하고 페이지를 접근하려는 유저의 id가 1인지 확인하는 것이다.
이것은 JWT와는 다른점 JWT는 암호화 되지 않고 서명이 되었을 뿐이다.
JWT는 user의 id를 가진 객체에 서명하고 이 서명과 함께 user에게 token을 보내는 것이다.
{
id:1
}
-----> sign ------> {id:1, signature:abc}
{id:1, signature:abc} ------> check sign -----> { id:1 }
user는 토큰을 받아서 살펴볼 수 있다. 유저가 JWT 토큰 안에 있는 정보를 훤히 알아볼 수 있다. 유저가 JWT를 보냈을 때 우리는 서명을 확인하고 그 token을 신뢰하는 형태이다.
또한 세션을 위한 백엔드를 구축할 필요가 없다는 것이다.
여기에서 쿠키를 만들고, 브라우저에게 보내는 등의 작업을 저희가 직접 할 필요가 없습니다. 이 작업들을 도와주는 helper function들을 Iron Session에서 제공합니다.
npm i iron-session
이 함수를 설명하기 전에, /api 폴더 내에 id가 1번인 user의 정보를 주는 API handler 함수를 다음과 같이 만들 수 있습니다. findUser 함수는 DB에서 id에 해당하는 user를 찾는 함수입니다.
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const id = 1;
const foundUser = findUser(id);
res.json({ ok: true, foundUser });
}
여기에서 id가 1번인 user가 아니라 현재 로그인 되어있는 user의 정보를 얻어 오려면 어떻게 해야할까요? 요청에서 id를 가져오는 작업이 필요하겠죠. 이를 도와주는 함수가 withIronSessionAPIRoute 입니다. 다음과 같이 코드를 변경해 봅시다.
import { NextApiRequest, NextApiResponse } from "next";
import { withIronSessionApiRoute } from "iron-session/next";
import { IronSessionOptions } from "iron-session";
const options: IronSessionOptions = {
cookieName: "reservation-site",
password:
"VERYYLONGPASSWORDS1ab1323898esda345q6781!",
};
async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const id = req.session.user.id;
const foundUser = await findUser(id);
res.json({ ok: true, foundUser });
}
export default withIronSessionApiRoute(handler, options);
이 함수의 두 인자는 다음과 같습니다.
const id = req.session.user.id;
요청의 쿠키에 user가 저장되어 있는 경우 위 코드와 같이 req.session을 이용해 가져올 수 있습니다.
만약 위 코드를 그대로 사용했다면 타입스크립트가 에러를 띄울 것 입니다. 세션 내의 데이터에 대한 정보를 타입스크립트는 모르기 때문입니다. 세션 데이터 타입 작성하는 법에 따라 다음과 같이 declare module 명령어를 이용해 작성합니다.
declare module "iron-session" {
interface IronSessionData {
user?: {
id: number;
};
}
}
다만 이 경우에는 세션의 user가 undefined가 될 수 있으므로 id를 가져올 때 session의 유저가 있는지 확인해주는 작업을 추가해주면 됩니다.
const id = req.session.user ? req.session.user.id : 1;
지금까지 세션에서 데이터를 어떻게 가져오는지에 대해서만 얘기했습니다. 이번에는 세션에 데이터를 어떻게 저장하는지에 대해 알아 봅시다.
POST 요청이 들어왔을 때, 요청에서 userId를 추출해서 세션에 저장하는 코드는 다음과 같습니다.
if (req.method === "POST") {
const {
body: { userId },
} = req;
// Assign value
req.session.user = {
id: +userId,
};
// Save at a session
await req.session.save();
res.json({ ok: true });
}
위와 같이 세션에 값을 할당한 후 save() 함수를 실행시킵니다. 이러면 데이터가 세션에 저장될 뿐만 아니라, 이후에 응답을 보낼때 해당 정보가 암호화되어 쿠키로 브라우저에 보내 집니다.
이후 다른 요청이 들어오면, 그 요청에는 지금 저장한 정보가 쿠키에 실려서 같이 올 것입니다.
그러면 이렇게 암호화 된 쿠키값을 확인 할 수 있다!!