쿠우보이님의 브런치를 보고, 글에 대해 한번 더 생각해보게 되었습니다.

개인적으로 정말 도움되는 글이었습니다.

🏆[쿠우보이님 : 좋은 질문] https://brunch.co.kr/@kooslab/199


JWT

이번 주차는 조금 바빠서 블로깅이 늦어졌습니다.

서버와 데이터베이스를 다루는 주차였는데요,

실제 스프린트에 적용못한 JWT를 블로깅으로 공부해보겠습니다.

YOUTUBE에서 가장 먼저 나오는 강의를 통해 공부해보았습니다.

[참고] : https://www.youtube.com/watch?v=7nafaH9SddU


🍪 기존의 세션 방식

session과 cookie만을 사용하여 서버를 만들다가,

프로젝트 뒷부분에서 JWT로 리팩토링을 해보는 과제가 있었습니다.

보통 로그인을 구현할 때를 가장 먼저 생각할 수 있는데요,

CLIENT - SERVER 순으로 연결되어서

클라이언트가 요청을 하면, 서버가 세션을 생성하고, 클라이언트로 응답합니다.

이후, 동일한 클라이언트가 요청을 하면 세션을 확인하여, 응답을 하죠.

이러한 방식은 유저가 인증할 때마다 서버에 요청을 하기 때문에 서버에 부담이 갑니다.

계속 서버의 메모리에 세션을 저장하므로, 요청 수가 늘어나면 서버가 과부하되는 것이죠.

DATABASE를 연결한다 해도, 데이터베이스의 성능에 문제가 생길 수 있습니다.

또한, 서버부담이 커져서 서버를 확장시켜야 할 때, 이 과정도 복잡하다고 합니다.

🥞 토큰 방식

토큰은 Stateless, 즉 상태를 유지하지 않습니다.

클라이언트가 요청을 하면, 서버가 이를 확인하고, 클라이언트로 토큰을 발급해줍니다.

토큰을 받은 클라이언트가 이를 저장해 놓고, 다음 요청 시에 토큰을 같이 전달합니다.

그럼, 서버는 이 토큰을 확인하여 클라이언트로 전달하는 것이죠!

🥠 JWT

그렇다면 이제 JWT를 알아볼까요?

JWT(JSON WEB TOKEN)은 전자자 서명된 URL-safe의 JSON입니다.

설명은 어렵지만, 토큰을 간편하게 다룰 수 있는 JSON형태의 방식이라고 생각하시면 됩니다.

생긴 것 부터 봅시다. JWT 홈페이지의 토큰 생성 툴입니다. ( https://jwt.io )

image.png

빨간부분이 Header, 보라색이 Payload, 하늘색이 Signature 입니다.

Header는 토큰의 타입과 해시 암호화 알고리즘으로 구성되어 있습니다.

'typ'는 토큰의 타입, alg'는 SHA256 같은 해시 알고리즘입니다.

Payload는 토큰에 담을 클레임 정보를 가지고 있습니다.

Payload에 담는 정보의 한 ‘조각’ 을 클레임이라고 부르고, {name: value} 형태입니다.

클레임의 정보는 registered, public, private 클레임으로 세 종류가 있습니다.

Signature는 secret key를 포함하여 암호화되어 있습니다.

🍩 jsonwebtoken

Javascript패키지인 jsonwebtoken을 사용하여 간단한 구현을 해보겠습니다.

먼저 express와 jsonwebtoken을 설치합니다.

# terminal
yarn init -y
yarn add express jsonwebtoken

이제 app.js 파일을 생성하고, 작성합니다.

사용할 모듈을 불러오고, 서버를 연결합니다.

// app.js
// TODO: Basic setting
const express = require("express");
const jwt = require("jsonwebtoken");
const app = express();
const port = 8000;

app.listen(port, () => console.log(`Server is Running on ${port}!!`));

이제 각 요청을 생성합니다. Postman으로 테스트해보는 것이 좋습니다 :)

  1. 먼저 GET요청으로 Hi, JWT 가 들어오는지 확인합니다.

  2. /api/posts에 post요청으로 토큰을 검증합니다. 유효하다면 정보를 전달합니다.

  3. /api/login에 post요청으로 user정보를 담아서 토큰을 생성합니다.

  4. secretKey는 원하는 암호로 설정하고, expiresIn은 만료 시간입니다.

// app.js

// ... 생략 ...

// TODO: GET
app.get("/api", (req, res) => {
  res.json({
    message: "Hi, JWT"
  });
});

// TODO: POST
app.post("/api/posts", verifyToken, (req, res) => {
  jwt.verify(req.token, "secretKey", (err, authData) => {
    if (err) {
      res.sendStatus(403);
    } else {
      res.json({
        message: "POST CREATED...!",
        authData
      });
    }
  });
});

// TODO: LOGIN
app.post("/api/login", (req, res) => {
  // Mock User
  const user = {
    id: 1,
    username: "geon",
    email: "geon@gmail.com"
  };
  jwt.sign({ user }, "secretKey", { expiresIn: "30s" }, (err, token) => {
    res.json({
      token
    });
  });
});

// 토큰 형식입니다.
// Authorization: Bearer <access_token>

// TODO: Verify Token
function verifyToken(req, res, next) {
  // Get auth header value
  const bearerHeader = req.headers["authorization"];
  // Check if bearer is undefined
  if (typeof bearerHeader !== "undefined") {
    // split at the space
    const bearer = bearerHeader.split(" ");
    // Get token from array
    const bearerToken = bearer[1];
    // Set the token
    req.token = bearerToken;
    // next middleware
    next();
  } else {
    // forbidden
    res.sendStatus(403);
  }
}});
// ... 생략 ...

이제 postman으로 확인해보겠습니다! 먼저 token을 발급받습니다.

image.png

이제 /api/posts 로 Key에는 Authorization, Value에는 Bearer + Token을 입력합니다.

결과를 한번 볼까요...?!

image.png

위 사진에서 정보를 보니 exp가 있네요...! 이것이 만료기한입니다.

30초 기다렸다가...다시 요청해보겠습니다!

image.png

else구문에서 만들었던 403 Forbidden 에러가 발생합니다.

이처럼 간단하게 토큰을 구현해볼 수 있었습니다 :)