본문 바로가기

E-commerce Vanila JS [8편] 로그인 인증(1)

by Recstasy 2021. 12. 10.

1 회원가입 UI제작

1) SigninScreen.js 추가하기

- 회원가입 라우팅 페이지를 랜더링하기 위해서 SigninScreen.js파일을 screens폴더아래에 추가한다.

 


  const SigninScreen = {
      after_render: () => {
 
      },
      render: () => {
          return `
              <div class="form-container">
                  <form id="signin-form">
                      <ul class="form-items">
                          <li>
                              <h1> Sign-In </h1>
                          </li>
                          <li>
                              <label for="email">Email</label>
                              <input type="email" name="email" id="email"/>
                          </li>
                          <li>
                              <label for="password">Password</label>
                              <input type="password" name="password" id="password"/>
                          </li>
                          <li>
                              <button type="submit" class="primary"> Signin </button>
                          </li>
                          <li>
                              <div>
                                  New User?
                                  <a href="/#/register">Create your account</a>
                              </div>
                          </li>
                      </ul>
                  </form>
              </div>
          `;
      },
  };
 
  export default SigninScreen;

[ root\frontend\src\screens\SigninScreen.js ]

 

 

2) index.js 추가하기

- 회원가입 라우팅을 추가한다.

 


 
import {parseRequestUrl} from './utils';
  import HomeScreen from './screens/HomeScreen';
  import ProductScreen from './screens/ProductScreen';
  import Error404Screen from './screens/Error404Screen';
  import CartScreen from './screens/CartScreen';
  import SigninScreen from './screens/SigninScreen'; // 추가...
 
  const routes = {
      '/' : HomeScreen,
      '/product/:id':ProductScreen,
      '/cart/:id': CartScreen,
      '/cart': CartScreen,
      '/signin': SigninScreen// 추가...
  }
 
  const router = async() => {
      const request = parseRequestUrl();
        // 중략...

[ root\frontend\src\index.js ]

 

 

3) 라우팅 실행

- 라우팅이 정상적으로 이뤄지는지 테스트한다.

- \root\frontend\> npm start

 

css미적용 회원가입 페이지

 

 

4) CSS적용하기

- 회원가입 폼에 css를 적용한다.

 


  /* Form */
    .form-container{
        display: flex;
        justify-content: center; 
        align-items
:flex-start;
        height:100%;
    }

    .form-items{
        display: flex;
        flex-direction: column;
        width: 32rem;
        padding: 2rem;
        border: 0.1rem #c0c0c0 solid;
        border-radius: 0.5rem;
        list-style-type:none;
    }

    .form-items li{
        display:flex;
        flex-direction: column;
        margin-bottom: 1rem;
        margin-top: 1rem;
    }

    .form-container h1{
        font-size: 2.5rem;
    }

[ root\frontend\style.css ]

 

flex를 적용한 결과는 아래와 같다.

 

[ css 적용후 ]

 

 

 

 

 


2 유저 로그인

1) 'body-parser', 'express-async-handler', 'jsonwebtoken' npm모듈 설치

- root\> npm install --save body-parser 

- root\> npm install --save express-async-handler

- root\> npm install --save jsonwebtoken

 

 

2) server.js 수정

- 로그인 창에 입력된 정보는 post방식으로 서버에 전달된다. body-parser모듈을 사용한다면, Post방식으로 전달된 데이터를 받을 수 있다. body-parser모듈을 추가하고, 미들웨어로 지정한다. 

 


 
import express from 'express';
  import cors from 'cors';
  import mongoose from 'mongoose';
  import config from './config';
  import bodyParser from 'body-parser' // 추가
  import data from './data.js';
  import userRouter from './routers/userRouter';
 
  dbMain().then( () => {
    // 중략...
  }
 
  const app = express();
 
  app.use(cors());
  app.use(bodyParser.json());     // 추가
  app.use('/api/users', userRouter );
  app.get('/api/products', (req, res) => {
      res.send(data.products);
  });
 
  app.get('/api/products/:id', (req, res) => {

    // 중략...
 

[ root\backend\server.js ]

 

 

3) userRouter.js 수정 & 추가

- 프로미스 구문을 express라우터에 적용하기 위해서 expressAsyncHandler모듈을 추가한다. 해당 모듈 덕분에 createUser값이 서버에 저장되기 전까지 res.send( )구문은 실행되지 않는다. 

 


 
import express from 'express';
  import User from '../models/userModel';
  import expressAsyncHandler from 'express-async-handler'// 추가
  import { generateToken } from '../utils'; // 추가
 
  const userRouter = express.Router();
 
  userRouter.get(
      '/createadmin',
      expressAsyncHandler(async(req, res) =>// 수정
          try{
              const user = new User({
                  name: 'admin',
                  email: 'blogtest@gmail.com',
                  password: 'blogtest@',
              });
              const createUser = await user.save();
              res.send(createUser);
          }catch(err){
              res.status(500).send({ message: err.message })
          }
      }) //expressAsyncHanlder End
  );
 

    // signin라우터 추가
  userRouter.post(    
      '/signin',
      expressAsyncHandler(async(req, res) => {  
          const signinUser = await User.findOne({
              email: req.body.email,
              password: req.body.password,
          });
          if(!signinUser){
              res.status(401).send({
                  message: 'Invalid Email or Password'
              })
          }else{
              res.send({
                  _id: signinUser._id,
                  name: signinUser.name,
                  email: signinUser.email,
                  isAdmin: signinUser.isAdmin,
                  token: generateToken(signinUser),
              })
          }
      }) //expressHandler End
  );
 
  export default userRouter;

[ root\backend\routers\userRouter.js ]

 

- '/signin' 라우터의 경우, 서버에 쿼리한 결과가 signinUser변수에 저장되며, 해당 반환값을 프론트에 전달한다.  

- 비밀번호를 암호화하는 jsonwebtoken 모듈이 없는 상태이므로 backend\폴더 아래에 서버용 utils.js 파일을 생성한다. 

 

 

4) 서버용 utils.js 생성

- jwt(json-web-token)모듈은 사용자 정보를 기반으로 토큰을 발행한다. jwt를 사용하면 간편하게 사용자 인증을 처리할 수 있다. 따로 세션을 만들지 않고, 발행된 토큰을 비교함으로써 인증을 진행하기 때문에 Auth보다 편리하다. 

 


  import
jwt from 'jsonwebtoken';
  import config from './config';

  export const generateToken = (user) => {
      return jwt.sign({
          _id: user._id,
          name: user.name,
          email: user.email,
          isAdmin: user.isAdmin
      }, config.JWT_SECRET )
  }

[ root\backend\utils.js ]

 

 

 

5) 서버용 config.js 생성

- 프론트에서 서버주소를 저장한 config.js를 생성한 것처럼 서버에서는 환경변수 데이터가 저장된 config.js가 필요하다. config.js에 사용되는 dotenv모듈은 process.env에 전역으로 접근할 수 있다. 

 


 
import dotenv from 'dotenv';
 
  dotenv.config();
 
  export default{
      MONGODB_URL: process.env.MONGODB_URL,
      JWT_SECRET: process.env.JWT_SECRET,
  }

[ root\backend\config.js ]

 

 

6) .env 추가

- JWT_SECRET변수를 추가한다.

 

  MONGODB_URL=mongodb://localhost/blogtest_db
  JWT_SECRET=somethingsecret

[ root\.env ]

 

 

7) server.js 미들웨어 추가

- 유효성 에러와 관련된 미들웨어를 server.js에 추가한다.

 


 
import express from 'express';

  // 중략...

  app.get('/api/products/:id', (req, res) => {
      const product = data.products.find((x) => x._id === req.params.id);
      if(product){
          res.send(product);
      }else{
          res.status(404).send({message: 'product Not Found!'});
      }
  });
 
  app.use((err, req, res, next) =>// <--추가
      const status = err.name && err.name === 'ValidationError' ? 400 : 500;
      res.status(status).send({ message: err.message });
  })  // 추가-->
 
  app.listen(5000, () => {
      console.log('server at http://localhost:5000');
  });

[ root\backend\server.js ]

 

 

8) SigninScreen.js 추가

- 로그인 인증이 끝난 뒤에 이뤄지는 기능(인덱스 페이지로 이동)을 추가한다.

 

  import { signin } from '../api';
 
  const SigninScreen = {
      after_render: () =>// <-- 추가
          document.getElementById('signin-form')
          .addEventListener('submit', async(e) => {
              e.preventDefault();
              const data = await signin({
                  email: document.getElementById('email').value,
                  password: document.getElementById('password').value,
              });
              if(data.error){
                  alert(data.error);
              }else{
                  document.location.hash = '/';
              }
          })  // 추가 -->
      },
      render: () => {
     
   // 중략...
 
  export default SigninScreen;

[ root\frontend\src\screens\SigninScreen.js ]

 

 

9) api.js 추가

- 로그인에 성공했을 때, POST방식으로 'localhost:5000/api/signin'경로에 있는 유저 정보를 받아올 수 있다. 인증 미들웨어를 통과한 상태에서 axios모듈은 POST방식으로 해당 경로에 접근할 수 있다. api.js의 signin함수는 axios를 통해 유저 정보를 반환한다. 

 


    // 중략...
 
export const signin = async({email, password}) =>// <-- 추가
      try{
          const response = await axios({
              url: `${apiUrl}/api/users/signin`,
              method: 'POST',
              header: {
                  'Content-Type': 'application/json',
              },
              data:{
                  email,
                  password
              },
          });
 
          if(response.statusText !== 'OK'){
              throw new Error(response.data.message);
          }
          return response.data;
      }catch(err){
          console.log(err);
          return { error: err.response.data.message || err.message }
      }
  }  // 추가 -->

[ root\backend\config.js ]

 

 

10) 테스트

- 관리자 아이디로 로그인을 진행해본다. 비밀번호나 아이디를 틀리게 입력했을 때, 아래와 같은 오류가 발생한다.

 

[ 관리자 정보오류 ]

 

 

userRouter.js에 입력한 관리자 아이디와 비밀번호를 정확하게 입력해주면, 아래와 같이 index페이지로 이동한다. 

 

[ 관리자 로그인 성공 ]

 

 

댓글

최신글 전체

이미지
제목
글쓴이
등록일