본문 바로가기

E-commerce Vanila JS [10편] 회원가입 | 인증 | 로그아웃

by Recstasy 2021. 12. 11.

 

1 유저 회원가입

1) RegisterScreen.js 생성

- SigninScreen.js파일을 복사해서 이름을 'RegisterScreen.js'로 변경한다.

 

 

2) RegisterScreen.js 수정

- Ctrl + F(비주얼스튜디오 기준) 단축키로 'SigninScreen'단어를 검색한 뒤 모두 'RegisterScreen'로 변경한다.

- 'signin'단어를 검색한 뒤 모두 'register'로 변경한다.

[ SigninScreen → RegisterScreen.js 변경 ]

 

- 폼 부분을 변경하고, name항목을 DB목록에 추가한다.

 


 
import { register } from '../api';
  import { getUserInfo, setUserInfo } from '../localStorage';
  import { showLoading, hideLoading, showMessage } from '../utils';
 
  const RegisterScreen = {
      after_render: () => {
          document.getElementById('register-form')
          .addEventListener('submit', async(e) => {
              e.preventDefault();
              showLoading();
 
              const data = await register({
                  name: document.getElementById('name').value,   // 추가
                  email: document.getElementById('email').value,
                  password: document.getElementById('password').value,
              });
 
              hideLoading();
             
              if(data.error){
                  showMessage(data.error);
              }else{
                  setUserInfo(data);
                  document.location.hash = '/';
              }
          })
      },
      render: () => {
          if(getUserInfo().name){
              document.location.hash = '/';
          }
          return `
              <div class="form-container">
                  <form id="register-form">
                      <ul class="form-items">

                   // <-- 추가
                          <li>
                              <h1> Create Account </h1>   
                          </li>
                          <li>
                            <label for="name">Name</label>
                            <input type="name" name="name" id="name"/>
                         </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>
                             <label for="repassword">Re-Enter-Password</label>
                             <input type="password" name="repassword" id="repassword"/>
                         </li>
                  // 추가 -->

                          <li>
                              <button type="submit" class="primary"> Register </button>
                          </li>
                          <li>

                        // <-- 수정
                              <div>
                                  Already have an account?
                                  <a href="/#/signin">Sign-In</a>
                              </div>
                       // 수정 -->

                          </li>
                      </ul>
                  </form>
              </div>
          `;
      },
  };
 
  export default RegisterScreen;

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

 

 

3) api.js 추가

- 로그인 사용자 데이터를 POST방식으로 요청한 뒤에 결과를 반환하는 register( )함수를 추가한다.

 


    
export const register = async({name, email, password}) => {
        try{
            const response = await axios({
                url: `${apiUrl}/api/users/register`,
                method: 'POST',
                header: {
                    'Content-Type': 'application/json',
                },
                data: {
                    name,
                    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\frontend\src\api.js ]  

 

 

4) index.js 추가

- /register 라우터를 추가한다.

 

// 중략...

    import RegisterScreen from './screens/RegisterScreen';
   
    const routes = {
      '/': HomeScreen,
      '/product/:id': ProductScreen,
      '/cart/:id': CartScreen,
      '/cart': CartScreen,
      '/signin': SigninScreen,
      '/register': RegisterScreen
    };
   
    for(let item in CartScreen){

  // 중략...

[ root\frontend\src\api.js ]  

 

 

5) userRouter.js 추가

- 사용자 등록폼에서 'submit'버튼을 클릭했을 때, 몽고DB 서버에 사용자가 입력한 폼데이터가 저장되어야 한다. 이와 관련된 기능은 backend\routers\userRouter.js가 담당한다.

 


  //중략...

 
userRouter.post(
      '/register',
      expressAsyncHandler(async(req, res) => {
          const user = new User({
              name: req.body.name,
              email: req.body.email,
              password: req.body.password,
          });
 
          const createUser = await user.save();
 
          if(!createUser){
              res.status(401).send({
                  message: 'Invalid Email or Password'
              })
          }else{
              res.send({
                  _id: createUser._id,
                  name: createUser.name,
                  email: createUser.email,
                  isAdmin: createUser.isAdmin,
                  token: generateToken(createUser),
              })
          }
      }) //expressHandler End
  );
 
  export default userRouter;

[ root\backend\routers\userRouter.js ]  

 

 

 

 

 


2 계정 업데이트

1) ProfileScreen.js 생성

- ProfileScreen.js는 사용자 계정을 업데이트할 수 있는 기능을 담고있다.

 


 
import { update } from '../api';
  import { getUserInfo, setUserInfo } from '../localStorage';
  import { showLoading, hideLoading, showMessage } from '../utils';
 
  const ProfileScreen = {
      after_render: () => {
       
          document
              .getElementById('profile-form')
              .addEventListener('submit', async(e) => {
                  e.preventDefault();
                  showLoading();
 
                  const data = await update({
                      name: document.getElementById('name').value,
                      email: document.getElementById('email').value,
                      password: document.getElementById('password').value,
                  });
                 
                  hideLoading();
                  if(data.error){
                      showMessage(data.error);
                  }else{
                      setUserInfo(data);
                      document.location.hash = '/';
                  }
              });
      },
      render: () => {
          const { name, email } = getUserInfo();
 
          return `
          <div class="form-container">
              <form id="profile-form">
                <ul class="form-items">
                  <li>
                    <h1>User Profile</h1>
                  </li>
                  <li>
                    <label for="name">Name</label>
                    <input type="name" name="name" id="name" value="${name}" />
                  </li>
                  <li>
                    <label for="email">Email</label>
                    <input type="email" name="email" id="email" value="${email}" />
                  </li>
                  <li>
                    <label for="password">Password</label>
                    <input type="password" name="password" id="password" />
                  </li>
                  <li>
                    <button type="submit" class="primary">Update</button>
                  </li>
                  <li>
                  <button type="button" id="signout-button" >Sign Out</button>
                </li>
 
                </ul>
              </form>
          </div>
          `;
      },
  };
 
  export default ProfileScreen;

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

 

 

2) api.js 추가

- 사용자가 정보를 업데이트 하는경우, 로컬 스토러지에 해당 정보를 저장하는 기능을 추가한다.

 


 
  export const register = async({name, email, password}) => {

    //중략...

  }

    //추가

 
export const update = async({name, email, password}) => {
      try{
          const { _id, token } = getUserInfo();
          const response = await axios({
              url: `${apiUrl}/api/users/${_id}`,
              method: 'PUT',
              headers:{
                  'Content-Type': 'application/json',
                  Authorization: `Bearer ${token}`,
              },
              data: {
                  name,
                  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\frontend\src\api.js ]  

 

 

3) index.js 추가

- /profile 라우터를 추가한다.

 


  //중략...

  import
ProfileScreen from './screens/ProfileScreen'  //추가

  const routes = {
      '/' : HomeScreen,
      '/product/:id':ProductScreen,
      '/cart/:id': CartScreen,
      '/cart': CartScreen,
      '/signin': SigninScreen,
      '/register': RegisterScreen,
      '/profile': ProfileScreen //추가
  }

  const router = async() => {
      showLoading();

  //중략...

[ root\frontend\src\index.js ]  

 

 

4) userRouter.js 추가

- userRouter.js는 유저가 업데이트한 데이터를 서버에 전달한다.

 

 
  userRouter.post(
      '/register',

      //중략...
  );

  // <-- 추가
 
userRouter.put(
      '/:id',
      isAuth,
      expressAsyncHandler(async(req, res) => {
          const user = await User.findById(req.params.id);
 
          if(!user){
              res.status(400).send({
                  message: 'User Not Found'
              });
          }else{
              user.name = req.body.name || user.name;
              user.email = req.body.email || user.email;
              user.password = req.body.password || user.password;
              const updateUser = await user.save();
 
              res.send({
                  _id: updateUser._id,
                  name: updateUser.name,
                  email: updateUser.email,
                  isAdmin: updateUser.isAdmin,
                  token: generateToken(updateUser),
              })
          }
      })
  )
  // 추가 -->
  export default userRouter;

[ root\backend\routers\userRouter.js ]  

 

 

 

 

 


3 Auth 인증

1) utils.js(backend) 추가

- header값에 authorization이 없다면, 에러를 반환하는 isAuth기능을 추가한다.

 


 
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 )
  }
  
  // <-- 추가
 
export const isAuth = (req, res, next) => {
      const bearerToken = req.headers.authorization;
      if(!bearerToken){
          res.status(401).send({ message: 'Token is not supplied'});
      }else{
          const token = bearerToken.slice( 7, bearerToken.length );
          jwt.verify( token, config.JWT_SECRET, (err, data) => {
              if(err){
                  res.status(401).send({ message: 'Invalid Token'});
              }else{
                  req.user = data;
                  next();
              }
          })
      }
  }
  // 추가 -->

[ root\backend\utils.js ]  

 

 

2) userRouter.js 추가

- utils.js에서 'isAuth'함수를 가져온 뒤, put()라우터의 미들웨어로 활용한다.

 


  // 중략...
  import { generateToken, isAuth } from '../utils'// 추가

  // 중략...
 
  userRouter.put(
      '/:id',
      isAuth// 추가
      expressAsyncHandler(async(req, res) => {
          const user = await User.findById(req.params.id);

      // 중략...
  )

  export default userRouter;

[ root\backend\utils.js ]  

 

 

3) 테스트 

- 아이디로 접속한 뒤에 이름을 변경해서 업데이트를 진행해보자.

 

[ 업데이트 실행 ]

 

- 업데이트가 성공적으로 반영되며, Request Headers에서 정상적으로 발급된 인증키를 확인할 수 있다.

 

 

 

 

 

 


4 로그아웃

1) ProfileScreen.js 추가

- after_render( )메서드에 로그아웃 버튼과 clearUser( )함수를 추가한다.

 


    import
 { update } from '../api';
    import { getUserInfo, setUserInfo, clearUser } from '../localStorage';      // clearUser 추가
    import { showLoading, hideLoading, showMessage } from '../utils';

    const ProfileScreen = {
        after_render: () => {

            document.getElementById('signout-button'  // 추가...
            .addEventListener('click', () => {
                clearUser();
                document.location.hash = '/';
            })

             document
                 .getElementById('profile-form')
   // 중략...

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

 

 

2) localStorage.js 추가

- 로그아웃 버튼을 클릭하게 되면, 로컬 스토러지에 저장된 현재의 유저정보가 삭제된다.

 


  // 중략...
  export
 const getUserInfo = () =>{
  // 중략...
  }

  // <-- 추가
  export const clearUser = () => {
      localStorage.removeItem('userInfo');
  }
  // 추가 -->

[ root\frontend\src\localStorage.js ]  

 

 

 

3) 테스트

- 로그아웃 버튼을 클릭했을 때, 정상적으로 로그아웃이 진행된다.

 

[ 로그아웃 클릭 → 로그아웃 ]

댓글

최신글 전체

이미지
제목
글쓴이
등록일