[ReactJS] 로그인 유지(2)

Joo Yeong Park·2020년 10월 13일
1

develop

목록 보기
5/8

프론트엔드는 ReactJS, 서버는 Node.js로 만들었다.

Server

폴더구조

간단히 내가 건드린(?) 폴더만 설명하자면

  • migrations
    • 여기는 sequelize cli를 이용해 만든 model들의 자동생성 파일들이 만들어진다.
  • models
    • index.js
    • todo.js // 내가 만든 모델 - db table
    • user.js // 내가 만든 모델 - db table
  • routes
    • todo.js //todo에 관련된 api들을 분리
    • user.js // user에 관련된 api들을 분리
  • app.js
  • util.js // jwt를 위한 파일로 아래에 자세한 내용이 나온다.

로그인 api

로그인 시 토큰을 만들어 내서 발급한다.route/user.js 에 로그인 api를 만들었다.

router.post("/login", (req, res, next)=>{  
  // body에 로그인할 id, pw를 담아서 보낸다
  var body = req.body
  
  // models는 상단에서 var models = require("../models") 해준 것 
  models.user.findAll({
    where:{
      user_id: body.id,
      user_pw: body.pw
    }
  })
  .then( result => {
    // jwt.sign(payload, secret, option, callbackFunc)
    var payload = {
      user_id: result[0].user_id
    }
    const secret = util.secret
    var option = {
      issuer: 'zu0',
      subject: 'todo_list_service_token'
    }

    // jwt으로 토큰 발급
    jwt.sign(payload, secret, option, function(err, token){
      if(err){
        res.json(util.successFalse(err.message, '아이디 혹은 비밀번호가 틀립니다.'))
      }
      else{
        res.json(util.successTrue(token))
      }
    })
  })
  .catch( err => {
    res.json(util.successFalse(err.message))
  })
});

위의 코드의 util은 따로 파일을 만들어서 넣어준 것인데, jwt와 결과를 return할 때 쓰려고 만들어놨다.

👇util.js 여기서 참고했다.

var jwt = require('jsonwebtoken');

var util = {};

util.secret = "주0"

util.successTrue = function (data) {
    return {
        success: true,
        message: null,
        errors: null,
        data: data
    };
};

util.successFalse = function (err, message) {
    if (!err && !message) message = 'data not found';
    return {
        success: false,
        message: message,
        errors: (err) ? util.parseError(err) : null,
        data: null
    };
};

util.parseError = function (errors) {
    var parsed = {};
    if (errors.name == 'ValidationError') {
        for (var name in errors.errors) {
            var validationError = errors.errors[name];
            parsed[name] = { message: validationError.message };
        }
    } else if (errors.code == '11000' && errors.errmsg.indexOf('username') > 0) {
        parsed.username = { message: 'This username already exists!' };
    } else {
        parsed.unhandled = errors;
    }
    return parsed;
};

util.verifyJWT = function (token) {

    var success = false;
    var result = '';

    try {
        // token 복호화
        decode = jwt.verify(token, util.secret);        
        success = true;
        result = decode;
    } catch (err) {
        result = err;
    }

    return {
        success: success,
        result: result
    };
};

module.exports = util;

token 이용

로그인시 발급된 토큰은 클라이언트에게 넘겨준다.
클라이언트는 필요한 api 호출시 headers에 x-access-token 안에 발급받은 토큰을 넣어 요청한다.

그러면 다른 api에서 이 토큰을 검증하여 다음으로 진행한다.

아래는 todo-list에 들어갈 todo를 생성하는 간단한 api이다.

router.put('/create_todo', function(req, res){
    var token = req.headers['x-access-token'] || req.query.token
    var result_jwt = util.verifyJWT(token)
    var decord = ''

    if(result_jwt.success){
        decord = result_jwt.result
    }
    else {
        return res.json(util.successFalse(result_jwt.result, 'token 검증 실패'))
    }

    var body = req.body

    models.todo.create({
        user_id: decode.user_id,
        contents: body.contents,
        is_completed: 0,
        is_deleted: 0
    })
    .then(result => {
        res.json(util.successTrue())
    })
    .then(err => {
        res.json(util.successFalse(err))
    })
});

여기서도 마찬가지로 util을 사용하여 토큰을 복호화한다!

이런식으로 하면 서버에서 할일은 끝난다.

ReactJS

여기서 해줄 것은 로그인해서 받은 토큰을 저장해두고 필요한 api 요청시 headers에 토큰을 담아 함께 보내는 것이다.

토큰을 저장하는 방법으로는 redux store와 localStorage 정도일 것 같다.

나는 일단 localStorage로 구현해보고 나중에 redux를 공부해보는 걸로!

여기서 잠깐🎊
localStorage와 sessionStorage 차이점?
: 둘 다 Web Storage이지만 localStorage는 데이터가 영구적으로 보관이 가능한 반면
sessionStorage는 윈도우 창이 꺼지면 데이터가 지워진다.

일단 App.js에서 토큰이 있으면(로그인한 상태면) main 페이지로 가고 토큰이 없으면 로그인 페이지로 가도록 설정한다.

import React from 'react'
import {HashRouter, Redirect, Route, Switch} from 'react-router-dom'
import Main from './routes/Main'
import Login from './routes/Login'

function App() {
  return (
    <div className="app">
      <HashRouter>
          {localStorage.getItem("token") ? 
            <Redirect exact to="/" /> 
          : 
            <Redirect to="/login" />
          }

          <Switch>
            <Route exact path="/" component={Main} />
            <Route path="/login" component={Login} />
          </Switch>
      </HashRouter>
    </div>
  );
}

export default App;

이 조건부 렌더링을 처음에는 <Switch>를 사용하지 않고 직접했는데 그렇게 하면 아무것도 동작하지 않게 된다. react에서 Route를 조건부 렌더링 할 때 <Switch>를 이용해야한다!

그리고 로그인 시 로그인에 성공하면 서버에서 받은 토큰으로 localStorage.setItem("token", 서버에서 받은 토큰) localStorage를 설정해주면 된다.

다른 api호출 시 axios하면서 headers에 토큰을 넣어주면 끝!

profile
웹 개발자를 꿈꾸는 삐약

0개의 댓글