기타/백엔드

nodejs api 인증 예제 ( JWT, sequelize, express )

에잇콘텐츠 2021. 3. 29. 08:05

백엔드 개발에서 가장 중요한 API 인증 예제입니다.

nodejs 언어를 사용하고, API 는 express 를 ORM (object relational mapping) 는 sequelize 를 사용했습니다.

api 인증을 위해서는 JWT (json web token) 를 사용했습니다.

 

회원가입, 로그인, 내정보보기의 3가지 API 를 구현했습니다.

 

1. 예제 소스코드 다운로드

github.com/vipick/nodejs-auth-example

 

2. 소스코드 주요 폴더 및 파일

app/config/config.js  개발 환경 및 운영서버에서 DB 엑세스 구별 
app/controllers/auth.controllers.js 인증 컨트롤러
app/models/index.js DB 엑세스 및 사용하는 모델 초기화 코드 
app/models/user.model.js user 모델 및 관계를 정의 : users 테이블 생성 코드
app/routes/index.js 라우터
app/routes/verifyJwtToken.js API 인증을 위한 미들웨어 코드
.env 외부에 노출되면 안되는 key 값을 저장합니다. (예: JWT secret, DB 아이디, 패스워드, ip주소 등)
보통 git으로 관리하지 않습니다. (.gitignore)
운영 서버에서는 .env-example 에서 이름 명을 .env로 변경하고 key 값을 작성합니다.  
.gitignore git 으로 관리하지 않는 파일/폴더를 지정
app.js 메인 
server.js app.js 를 실행
package.json 외부 프로그램 버전 관리

실제 코드 작성이 많이 필요한 곳은 라우터, 컨트롤러, 모델 이렇게 3군데 입니다.

나머지 코드는 세팅 부분으로 많은 수정이 없습니다.

 

2-1) 라우터 

- routes 폴더의 index.js 파일에서 각각의 api 를 정의합니다. 

- 내 정보 api 미들웨어에서 인증 토큰 값을 검증합니다.

- 인증 토큰 값은 로그인 시 얻게 되고, 백엔드 인증 api를 요청할 때 토큰 값을 포함시켜서 보내면 인증이 필요한 백엔드 기능을 사용할 수 있습니다.  

2-2) 컨트롤러(Controller)

- 회원가입 시 bcrypt 함수로 암호화를 합니다.

- 로그인 시 회원정보 가(아이디, 패스워드) 일치 하면 jwt secret key 값을 활용해서 token 값을 생성 합니다. 86400 숫자는 24시간 만 유효하다는 것 입니다. 이 시간이 지나면 재로그인 필요합니다.

- User 모델을 사용해서 users DB 테이블에서 회원가입(create), 조회(findOne)를 진행했습니다. 

- 모델에 대한 추가적인 공부 구글에서 sequelize 키워드를 검색하면 됩니다.

const { User } = require("../models");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");

/**
 * 회원가입
 */
exports.signup = async (req, res) => {
  try {
    const user = await User.create({
      email: req.body.email,
      password: bcrypt.hashSync(req.body.password, 8),
      type: req.body.type,
    });

    if (user) {
      return res.status(201).send("회원가입 성공!");
    }
  } catch (err) {
    return res.status(500).send("회원가입 실패!" + err);
  }
};

/**
 * 로그인
 */
exports.signin = async (req, res) => {
  let user = null;
  try {
    user = await User.findOne({
      where: {
        email: req.body.email,
      },
    });
    if (!user) {
      return res.status(404).send("User Not Found.");
    }
  } catch (err) {
    return res.status(500).send("Error -> " + err);
  }

  const passwordIsValid = bcrypt.compareSync(req.body.password, user.password);
  if (!passwordIsValid) {
    return res.status(401).send({
      auth: false,
      accessToken: null,
      reason: "Invalid Password!",
    });
  }

  const token = jwt.sign(
    {
      id: user.id,
    },
    process.env.JWT_SECRET,
    {
      expiresIn: 86400, // expires in 24 hours
    }
  );

  return res.status(200).send({
    auth: true,
    accessToken: token,
  });
};

/**
 * 내 정보 보기
 */
exports.me = async (req, res) => {
  try {
    const user = await User.findOne({
      attributes: ["type"],
      where: {
        id: req.userId,
      },
    });
    if (user) {
      return res.status(200).json({
        description: "회원정보 보기",
        content: user,
      });
    }
  } catch (err) {
    return res.status(500).send("Error -> " + err);
  }
};