노력이 좋아서

<step94>'react-고객관리 사이트(로그인 구현_COOKIE)'

zoaseo 2022. 8. 3. 08:58

2022.07.18 - [공부가 좋아서] - 'redux(react)_고객관리 사이트_redux로 구현'

 

<step82>'redux(react)_고객관리 사이트_redux로 구현'

2022.07.08 - [공부가 좋아서] - 'react_고객관리 사이트 구현_step_final' 'react_고객관리 사이트 구현_step_final'" data-og-description="1) green_customer_client components/ CreateCustomer.js import Rea..

zoaseo.tistory.com

1) 

 

2)

서버구현

1. join 경로 post 요청시

customer_members 테이블에 데이터 추가하기

비밀번호는 암호화하여 저장

등록일은 Now()함수를 사용해서 현재 날짜를 저장

패스워드 암호화 알고리즘 라이브러리 - bcrypt 설치

npm install bcrypt

 

3)

https://www.npmjs.com/package/bcrypt

 

bcrypt

A bcrypt library for NodeJS.. Latest version: 5.0.1, last published: a year ago. Start using bcrypt in your project by running `npm i bcrypt`. There are 3418 other projects in the npm registry using bcrypt.

www.npmjs.com

4)

sever 구현

const express = require("express");
const cors = require("cors");
const app = express();
const port = 3001;
const mysql = require("mysql");
const fs = require("fs");

const bcrypt = require('bcrypt');
const saltRounds = 10;

const dbinfo = fs.readFileSync('./database.json');
// 받아온 json데이터를 객체형태로 변경 JSON.parse
const conf = JSON.parse(dbinfo);

// connection mysql연결 createConnection()
// connection.connect() 연결하기
// connection.end() 연결종료
// connection.query('쿼리문', callback함수)
// callback(error, result, result의 field정보)

const connection = mysql.createConnection({
    host: conf.host,
    user: conf.user,
    password: conf.password,
    port: conf.port,
    database: conf.database,
})

// 회원가입 요청
app.post("/join", async (req, res)=>{
    // green1234
    let myPlaintextPass = req.body.userpass;
    let myPass = "";
    if(myPlaintextPass != '' && myPlaintextPass != undefined){
        bcrypt.genSalt(saltRounds, function(err, salt) {
            bcrypt.hash(myPlaintextPass, salt, function(err, hash) {
                // Store hash in your password DB.
                myPass = hash;
                console.log(myPass);
            });
        });
    }
})

암호화 되어 나타난다.

// 회원가입 요청
app.post("/join", async (req, res)=>{
    // green1234
    let myPlaintextPass = req.body.userpass;
    let myPass = "";
    if(myPlaintextPass != '' && myPlaintextPass != undefined){
        bcrypt.genSalt(saltRounds, function(err, salt) {
            bcrypt.hash(myPlaintextPass, salt, function(err, hash) {
                // Store hash in your password DB.
                myPass = hash;
                console.log(myPass);
                // 쿼리 작성
                const {username, userphone, userorg, usermail} = req.body;
                connection.query("insert into customer_members(username, userpass, userphone, userorg, usermail, regdate) values(?,?,?,?,?,DATE_FORMAT(now(),'%Y-%m-%d'))",
                    [username, myPass, userphone, userorg, usermail],
                    (err, result, fields) => {
                        console.log(result)
                        console.log(err)
                        res.send("등록되었습니다.")
                    }
                )
            });
        });
    }
})

5)

login 경로 post 요청시

usermail 값으로 일치하는 데이터를 조회(select)

입력받은 userpass를 암호화하여 조회한 데이터의 암호화된 값과

비교하여 일치하는지 확인

일치하면 조회한 데이터를 응답해줌

 

client 구현

components/Header.js

import React from 'react';
import { Link } from 'react-router-dom';

const Header = () => {
    return (
        <div id="header">
            <h1>그린고객센터</h1>
            <ul>
                <li><Link to="/">고객리스트보기</Link></li>
                <li><Link to="/write">신규 고객 등록하기</Link></li>
                <li><Link to="/login">로그인</Link></li>
                <li><Link to="/join">회원가입</Link></li>
                <li>고객 검색</li>
            </ul>
        </div>
    );
};

export default Header;

components/JoinForm.js

import React, { useState } from 'react';
import { Table, TableBody, TableRow, TableCell } from '@mui/material';
import axios from 'axios';
import { API_URL } from '../config/conf';
import { useNavigate } from 'react-router-dom';

const JoinForm = () => {
    const navigate = useNavigate();
    const [formData, setFormData ] = useState({
        username: "",
        userpass: "",
        userpassck: "",
        userphone: "",
        userorg: "",
        usermail: ""
    })
    const onChange = (e) => {
        const { name, value } = e.target;
        setFormData({
            ...formData,
            [name]:value
        })
    }
    // 폼 submit 이벤트
    const onSubmit = (e) => {
        // form에 원래 연결된 이벤트를 제거
        e.preventDefault();
        console.log(formData);
        // 전화번호가 숫자인지 체크하기
        if(isNaN(formData.userphone)){
            alert("전화번호는 숫자만 입력해주세요");
            setFormData({
                ...formData,
                userphone: "",
            })
        }
        // input에 값이 있는지 체크하고
        // 입력이 다되어있으면 post전송
        else if(formData.username !== "" && formData.userpass !== "" &&
        formData.userphone !== "" && formData.userpassck !== "" &&
        formData.userorg !== "" && formData.usermail !== ""){
            addMember();
        }
    }
    function addMember() {
        axios.post(`${API_URL}/join`,formData)
        .then(res=>{
            alert('등록되었습니다.');
            navigate('/');
        })
        .catch(e=>{
            console.log(e);
        })
    }
    return (
        <div>
            <h2>신규고객등록하기</h2>
            <form onSubmit={onSubmit}>
                <Table>
                    <TableBody>
                        <TableRow>
                            <TableCell>이름</TableCell>
                            <TableCell>
                                <input name="username" type="text" 
                                value={formData.username}
                                onChange={onChange}/>
                            </TableCell>
                        </TableRow>
                        <TableRow>
                            <TableCell>비밀번호</TableCell>
                            <TableCell>
                                <input name="userpass" type="text" 
                                value={formData.userpass}
                                onChange={onChange}/>
                            </TableCell>
                        </TableRow>
                        <TableRow>
                            <TableCell>비밀번호 체크</TableCell>
                            <TableCell>
                                <input name="userpassck" type="text" 
                                value={formData.userpassck}
                                onChange={onChange}/>
                            </TableCell>
                        </TableRow>
                        <TableRow>
                            <TableCell>연락처</TableCell>
                            <TableCell>
                                <input name="userphone" type="text" 
                                value={formData.userphone}
                                onChange={onChange}/>
                            </TableCell>
                        </TableRow>
                        <TableRow>
                            <TableCell>이메일</TableCell>
                            <TableCell>
                                <input name="usermail" type="text" 
                                value={formData.usermail}
                                onChange={onChange}/>
                            </TableCell>
                        </TableRow>
                        <TableRow>
                            <TableCell>소속</TableCell>
                            <TableCell>
                                <input name="userorg" type="text" 
                                value={formData.userorg}
                                onChange={onChange}/>
                            </TableCell>
                        </TableRow>
                        <TableRow>
                            <TableCell colSpan={2}>
                                <button type="submit">등록</button>
                                <button type="reset">취소</button>
                            </TableCell>
                        </TableRow>
                    </TableBody>
                </Table>                
            </form>
        </div>
    );
};

export default JoinForm;

server

// 로그인 요청
app.post('/login', async (req, res)=> {
    // usermail 값에 일치하는 데이터가 있는지 select문
    // userpass 암호화해서 쿼리 결과의 패스워드랑 일치하는지를 체크
    const { usermail, userpass } = req.body;
    connection.query(`select * from customer_members where usermail = '${usermail}'`,
        (err, rows, fields)=>{
            if(rows != undefined){
                if(rows[0] == undefined){
                    res.send(null)
                }else {
                    // Load hash from your password DB.
                    bcrypt.compare(userpass, rows[0].userpass, function(err, result) {
                        // result == true
                        if(result == true){
                            res.send(rows[0])
                        }else {
                            res.send("실패")
                        }
                    });
                }
            }else {
                res.send(null)
            }
        }
    )
})

 

 

1. 로그인 리듀서 작성

modules/logincheck.js

// 리덕스 액션타입, 액션 생성 함수, 초기값, 리듀서
const SET_LOGIN = 'SET_LOGIN';
const SET_LOGOUT = 'SET_LOGOUT';

// 액션 생성함수
export const setLogin = () => ({
    type: SET_LOGIN
})
export const setLogout = () => ({
    type: SET_LOGOUT
})

// 초기값 설정
const initialState = {
    isLogin: false
}
// 홈으로 이동함수
export const goToHome = (navigate) => () => {
    navigate('/');
}
// 리듀서 만들기
export default function logincheck(state=initialState, action){
    switch(action.type){
        case SET_LOGIN:
            return {
                isLogin: true
            }
        case SET_LOGOUT:
            return {
                isLogin: false
            }
        default:
            return state;
    }
}

modules/index.js

import { combineReducers } from "redux";
import logincheck from "./logincheck";

const rootReducer = combineReducers({logincheck});
export default rootReducer;

2. Login 컴포넌트 생성

3. util 폴더 안에 cookie.js 생성

npm install react-cookie

util/cookie.js

import { Cookies } from 'react-cookie';

const cookies = new Cookies();

// 쿠키 생성함수
export const setCookie = (name,value,options)=> {
    return cookies.set(name, value, {...options});
}

// 쿠키 접근함수
export const getCookie = (name) => {
    return cookies.get(name);
}

// 쿠키 삭제함수
export const removeCookie = (name) => {
    return cookies.remove(name);
}

components/Login.js

import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import axios from 'axios';
import { API_URL } from '../config/conf';
import { setCookie } from '../util/cookie';
import { useDispatch } from 'react-redux';
import { goToHome, setLogin } from '../modules/logincheck';

const Login = () => {
    const dispatch = useDispatch();
    const navigate = useNavigate();
    const [ loginData, setLoginData ] = useState({
        usermail: "",
        userpass: ""
    })
    const onChange = (e) => {
        const { name, value } = e.target;
        setLoginData({
            ...loginData,
            [name]:value
        })
    }
    const onSubmit = (e) => {
        e.preventDefault();
        // 인풋에 입력했는지 체크
        if(loginData.usermail === '' || loginData.userpass === ''){
            alert('이메일과 비밀번호를 입력해주세요');
        }else {
            axios.post(`${API_URL}/login`, loginData)
            // 로그인이 되었을 때
            .then(result=>{
                let { usermail, username } = result.data;
                console.log(result);
                // usermail에 값이 있을 때
                if(usermail !== null && usermail !== '' && usermail !== undefined){
                    alert('로그인되었습니다.');
                    // 현재시간 객체를 생성
                    let expires = new Date();
                    // 60분 더한 값으로 변경
                    expires.setMinutes(expires.getMinutes()+60);
                    setCookie('usermail', `${usermail}`, {path: '/', expires});
                    setCookie('username', `${username}`, {path: '/', expires});
                    dispatch(setLogin());
                    dispatch(goToHome(navigate));
                }
            })
            .catch(e=>{
                alert('이메일과 비밀번호를 확인해주세요');
            })
        }
    }
    return (
        <div>
           <form onSubmit={onSubmit}>
                <p><input type="text" name="usermail" value={loginData.usermail} onChange={onChange}/></p>
                <p><input type="password" name="userpass" value={loginData.userpass} onChange={onChange}/></p>
                <p>
                    <button type="submit">로그인</button>
                    <Link to="/join"><button>회원가입</button></Link>
                </p>
            </form>
        </div>
    );
};

export default Login;

components/Header.js

import React, { useEffect } from 'react';
import { Link } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { getCookie, removeCookie } from '../util/cookie';
import { setLogout } from '../modules/logincheck';

const Header = () => {
    const uname = getCookie('username');
    const isLogin = useSelector(state=>state.logincheck.isLogin);
    const dispatch = useDispatch();
    const logoutClick = () => {
        removeCookie('username');
        removeCookie('usermail');
        dispatch(setLogout());
    }
    useEffect(()=>{},[isLogin]);
    return (
        <div id="header">
            <h1>그린고객센터</h1>
            <ul>
                <li><Link to="/">고객리스트보기</Link></li>
                <li><Link to="/write">신규 고객 등록하기</Link></li>
                { isLogin && 
                <>
                    <li>{uname}님 환영!</li>
                    <li onClick={logoutClick}>로그아웃</li>
                    <li>회원정보수정</li>
                </>
                }
                { isLogin || 
                <>
                    <li><Link to="/login">로그인</Link></li>
                    <li><Link to="/join">회원가입</Link></li>
                </>
                }

                <li>고객 검색</li>
            </ul>
        </div>
    );
};

export default Header;

App.js

import './App.css';
import DetailCustomer from './components/DetailCustomer';
import Footer from './components/Footer';
import Header from './components/Header';
import { Route, Routes } from 'react-router-dom';
import EditCustomer from './components/EditCustomer';
import CustomerContainer from './components/CustomerContainer';
import CreateCustomerContainer from './components/CreateCustomerContainer';
import JoinForm from './components/JoinForm';
import Login from './components/Login';

function App() {
  return (
    <div className="App">
      <Header/>
      <Routes>
        <Route path="/" element={<CustomerContainer />}/>
        <Route path="/detailview/:no" element={<DetailCustomer/>}/>
        <Route path="/write" element={<CreateCustomerContainer/>}/>
        <Route path="/editCustomer/:no" element={<EditCustomer/>}/>
        <Route path="/join" element={<JoinForm/>}/>
        <Route path="/login" element={<Login/>}/>
      </Routes>
      <Footer/>
    </div>
  );
}

export default App;

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
import { applyMiddleware, legacy_createStore as createStore } from 'redux';
import rootReducer from './modules';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { CookiesProvider } from 'react-cookie';

const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
console.log(store.getState());
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
  <React.StrictMode>
    <Provider store={store}>
      <CookiesProvider>
        <App />
      </CookiesProvider>
    </Provider>
  </React.StrictMode>
  </BrowserRouter>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();